Created
December 28, 2022 00:17
-
-
Save danield137/26911863126cbbe1991b604b610b112b to your computer and use it in GitHub Desktop.
Example of locking a write to resource, and providing an awaitable for readers
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
using System.Collections.Concurrent; | |
using System.Threading.Tasks; | |
namespace ProperLockingTester | |
{ | |
public class WellLockedResource | |
{ | |
private readonly object m_lock; | |
private bool lockedState = false; | |
private TaskCompletionSource<bool> m_taskCompletionSource; | |
public WellLockedResource() | |
{ | |
m_lock = new object(); | |
} | |
public Task<bool> ProtectedActionAsync(bool withWait = false) | |
{ | |
Monitor.Enter(m_lock); | |
var messageFormat = "{0} | {1} | {2}"; | |
try | |
{ | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $"Entered Lock. state = '{lockedState}'"); | |
// no lock required, just skip to waiting | |
if (lockedState == true) | |
{ | |
Monitor.Exit(m_lock); | |
if (withWait && m_taskCompletionSource != null) | |
{ | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $" Wait requested"); | |
return m_taskCompletionSource.Task; | |
} | |
else | |
{ | |
return Task.FromResult(true); | |
} | |
} | |
if (withWait) | |
{ | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $" <!> Ugrade lock <!>"); | |
if (lockedState == false) | |
{ | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $" Mutation - WAIT"); | |
Thread.Sleep(1000); | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $" Mutation - EXEC "); | |
m_taskCompletionSource = new TaskCompletionSource<bool>(); | |
lockedState = true; | |
Monitor.Exit(m_lock); | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $" <!> Downgrade lock <!>"); | |
Thread.Sleep(3000); | |
if (!m_taskCompletionSource.Task.IsCompleted) | |
{ | |
m_taskCompletionSource.SetResult(true); | |
} | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $" Mutation exit"); | |
} | |
} | |
} | |
finally | |
{ | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $"Exit lock"); | |
if (Monitor.IsEntered(m_lock)) | |
{ | |
Monitor.Exit(m_lock); | |
} | |
} | |
return Task.FromResult(true); | |
} | |
} | |
public class SillyTest | |
{ | |
public static void SillyLockTest() | |
{ | |
var messageFormat = "{0} | {1} | {2}"; | |
var resource = new WellLockedResource(); | |
ConcurrentBag<Task<bool>> bools = new ConcurrentBag<Task<bool>>(); | |
// Enter a slow write lock | |
Task.Run(() => { bools.Add(resource.ProtectedActionAsync(true)); }); | |
Thread.Sleep(100); | |
// Enter a slow read lock | |
Task.Run(() => { bools.Add(resource.ProtectedActionAsync(true)); }); | |
// Enter another write lock - should wait to finish | |
// Enter a fast read lock | |
Task.Run(() => { bools.Add(resource.ProtectedActionAsync(false)); }); | |
Thread.Sleep(100); | |
// Enter another write lock - should wait to finish | |
Task.Run(() => { bools.Add(resource.ProtectedActionAsync(false)); }); | |
// Enter a slow read lock | |
Task.Run(() => { bools.Add(resource.ProtectedActionAsync(true)); }); | |
// Enter another write lock - should wait to finish | |
// Enter a fast read lock | |
Thread.Sleep(3000); | |
Task.Run(() => { bools.Add(resource.ProtectedActionAsync(false)); }); | |
Console.WriteLine(messageFormat, DateTime.Now, Thread.CurrentThread.ManagedThreadId, $"--- WAITING ---"); | |
Task.WhenAll(bools.ToArray()).ConfigureAwait(false).GetAwaiter().GetResult(); | |
Console.ReadLine(); | |
} | |
} | |
internal class Program | |
{ | |
static void Main(string[] args) | |
{ | |
SillyTest.SillyLockTest(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment