Created
March 24, 2024 19:38
-
-
Save controlflow/4b1c4e4c0d99329d9b1c092c956fdc9b to your computer and use it in GitHub Desktop.
This file contains 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; | |
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