Created
September 13, 2022 09:40
-
-
Save jessiepathfinder/64539b3c5ceedb7a6c2c0f7cc7a29b73 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// An extremely lightweight implementation of async mutexes | |
/// </summary> | |
public sealed class AsyncMutex{ | |
private readonly object locker = new object(); | |
private volatile int locked; | |
private readonly Queue<Action<bool>> queue = new Queue<Action<bool>>(); | |
public bool TryEnter() => Interlocked.Exchange(ref locked, 1) == 0; | |
/// <summary> | |
/// Returns a task that completes once we have entered the lock | |
/// </summary> | |
public Task Enter(){ | |
//If the lock is available, we return on the spot | |
if(TryEnter()){ | |
return Misc.completed; | |
} else{ | |
TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.None); | |
lock (locker) | |
{ | |
if (locked == 0) | |
{ | |
//It's very unlikely for us to get here | |
locked = 1; | |
return Misc.completed; | |
} | |
else | |
{ | |
//Add us to the queue of awaiters | |
queue.Enqueue(taskCompletionSource.SetResult); | |
return taskCompletionSource.Task; | |
} | |
} | |
} | |
} | |
public void Exit(){ | |
lock(locker){ | |
if(locked == 0){ | |
throw new InvalidOperationException("Mutex already unlocked"); | |
} else{ | |
if(queue.TryDequeue(out Action<bool> next)){ | |
//Hand over lock | |
next(false); | |
} else{ | |
//Release lock | |
locked = 0; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment