Skip to content

Instantly share code, notes, and snippets.

@controlflow
Created March 24, 2024 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save controlflow/4b1c4e4c0d99329d9b1c092c956fdc9b to your computer and use it in GitHub Desktop.
Save controlflow/4b1c4e4c0d99329d9b1c092c956fdc9b to your computer and use it in GitHub Desktop.
using System;
using System.Threading;
using JetBrains.Annotations;
namespace JetBrains.Util.Concurrency.Threading;
/// <summary>
/// Reader-writer lock for reader-heavy scenarios.
/// </summary>
public sealed class StripedReaderWriterLock
{
private readonly object[] myLocks;
private readonly int myLockIndexMask;
public StripedReaderWriterLock(int degreeOfReaderParallelism)
{
if (degreeOfReaderParallelism < 1)
throw new ArgumentOutOfRangeException(nameof(degreeOfReaderParallelism));
degreeOfReaderParallelism = RoundToPowerOfTwoAbove(degreeOfReaderParallelism);
var locks = new object[degreeOfReaderParallelism];
for (var index = 0; index < locks.Length; index++)
{
locks[index] = new object();
}
myLocks = locks;
myLockIndexMask = degreeOfReaderParallelism - 1;
static int RoundToPowerOfTwoAbove(int value)
{
if (value <= 0) return 1;
var mostSignificantBitPosition = 0;
while (value > 0)
{
mostSignificantBitPosition++;
value >>= 1;
}
return 1 << mostSignificantBitPosition;
}
}
public StripedReaderWriterLock()
: this((int)ProcessorUtil.GetProcessorCountWithAffinityMask())
{
}
[MustDisposeResource]
public ReadLockCookie UsingReadLock()
{
var threadId = Environment.CurrentManagedThreadId;
var lockObject = myLocks[threadId & myLockIndexMask];
Monitor.Enter(lockObject);
return new ReadLockCookie(lockObject);
}
public readonly ref struct ReadLockCookie
{
private readonly object myLockObject;
internal ReadLockCookie(object lockObject)
{
myLockObject = lockObject;
}
public void Dispose()
{
Monitor.Exit(myLockObject);
}
}
[MustDisposeResource]
public WriteLockCookie UsingWriteLock()
{
foreach (var lockObject in myLocks)
{
Monitor.Enter(lockObject);
}
return new WriteLockCookie(myLocks);
}
public readonly ref struct WriteLockCookie
{
private readonly object[] myLockObjects;
internal WriteLockCookie(object[] lockObjects)
{
myLockObjects = lockObjects;
}
public void Dispose()
{
foreach (var lockObject in myLockObjects)
{
Monitor.Exit(lockObject);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment