Skip to content

Instantly share code, notes, and snippets.

@jRimbault
Last active May 9, 2021 10:58
Show Gist options
  • Save jRimbault/d2640e9d8ff3b998d66fbc4a57cf7e0b to your computer and use it in GitHub Desktop.
Save jRimbault/d2640e9d8ff3b998d66fbc4a57cf7e0b to your computer and use it in GitHub Desktop.
a RAII mutex lock using the `IDisposable` interface
using System;
using System.Threading;
namespace Sync
{
// If C# had different (slightly better imo) visibility semantics
// that interface wouldn't exists and the nested type wouldn't need to be nested.
// The "best" we can do is use the repo version, put it in its own project and use
// the `internal` visibility, which is dumb shit.
public interface IMutexGuard<T> : IDisposable
where T : notnull
{
public T Value { get; }
}
public sealed class Mutex<T>
where T : notnull
{
private readonly object _monitor = new object();
private readonly T _inner;
public Locker(T value) => _inner = value;
public IMutexGuard<T> Lock()
{
if (!Monitor.TryEnter(_monitor, Timeout.Infinite))
{
throw new Exception($"Failed to acquire lock for {typeof(T)}");
}
return new MutexGuard<T>(this);
}
private void Unlock()
{
Monitor.Exit(_monitor);
}
private sealed class MutexGuard<U> : IMutexGuard<U>
where U : notnull
{
public U Value { get => _parent._inner; }
private bool _isDisposed = false;
private readonly Mutex<U> _parent;
public MutexGuard(Locker<U> parent) => _parent = parent;
public void Dispose()
{
if (_isDisposed)
{
return;
}
_parent.Unlock();
_isDisposed = true;
}
}
}
}
using Sync;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Mutex<List<(int, int)>> list = new(new());
// without the lock we'd get a concurrency exception :
// System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'
Task.WaitAll(Task.Run(() =>
{
using var guard = list.Lock();
foreach (var i in Enumerable.Range(1, 2000))
{
guard.Value.Add((i, i));
}
}),
Task.Run(() =>
{
using var guard = list.Lock();
foreach (var pair in guard.Value)
{
Console.WriteLine(pair);
}
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment