mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
mutexes are created before starting the menu or bootstrapper to ensure only one instance of them are running also menu can be opened without having to close bloxstrap background processes also fixed roblox singleton mutex not being created properly its 12 am and im fucking tired
102 lines
3.3 KiB
C#
102 lines
3.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Bloxstrap.Helpers
|
|
{
|
|
// https://gist.github.com/dfederm/35c729f6218834b764fa04c219181e4e
|
|
public sealed class AsyncMutex : IAsyncDisposable
|
|
{
|
|
private readonly string _name;
|
|
private Task? _mutexTask;
|
|
private ManualResetEventSlim? _releaseEvent;
|
|
private CancellationTokenSource? _cancellationTokenSource;
|
|
|
|
public AsyncMutex(string name)
|
|
{
|
|
_name = name;
|
|
}
|
|
|
|
public Task AcquireAsync(CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
TaskCompletionSource taskCompletionSource = new();
|
|
|
|
_releaseEvent = new ManualResetEventSlim();
|
|
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
|
|
// Putting all mutex manipulation in its own task as it doesn't work in async contexts
|
|
// Note: this task should not throw.
|
|
_mutexTask = Task.Factory.StartNew(
|
|
state =>
|
|
{
|
|
try
|
|
{
|
|
CancellationToken cancellationToken = _cancellationTokenSource.Token;
|
|
using var mutex = new Mutex(false, _name);
|
|
try
|
|
{
|
|
// Wait for either the mutex to be acquired, or cancellation
|
|
if (WaitHandle.WaitAny(new[] { mutex, cancellationToken.WaitHandle }) != 0)
|
|
{
|
|
taskCompletionSource.SetCanceled(cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
catch (AbandonedMutexException)
|
|
{
|
|
// Abandoned by another process, we acquired it.
|
|
}
|
|
|
|
taskCompletionSource.SetResult();
|
|
|
|
// Wait until the release call
|
|
_releaseEvent.Wait();
|
|
|
|
mutex.ReleaseMutex();
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
taskCompletionSource.TrySetCanceled(cancellationToken);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
taskCompletionSource.TrySetException(ex);
|
|
}
|
|
},
|
|
state: null,
|
|
cancellationToken,
|
|
TaskCreationOptions.LongRunning,
|
|
TaskScheduler.Default);
|
|
|
|
return taskCompletionSource.Task;
|
|
}
|
|
|
|
public async Task ReleaseAsync()
|
|
{
|
|
_releaseEvent?.Set();
|
|
|
|
if (_mutexTask != null)
|
|
{
|
|
await _mutexTask;
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
// Ensure the mutex task stops waiting for any acquire
|
|
_cancellationTokenSource?.Cancel();
|
|
|
|
// Ensure the mutex is released
|
|
await ReleaseAsync();
|
|
|
|
_releaseEvent?.Dispose();
|
|
_cancellationTokenSource?.Dispose();
|
|
}
|
|
}
|
|
}
|