ReadWriteLock
ReadWriteLock Implementation
This code provides an implementation of a read-write lock. It allows multiple readers or a single writer to access a resource, but not both. The implementation includes spin-locking and yielding to optimize performance. It supports recursive locking for write operations but not for read operations to write operations. The main program demonstrates a simple use case with a writer and a reader task.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ServerCore
{
/// <summary>
/// 1. No recursive locking allowed.
/// 2. Spin lock policy (5000 spins -> yield).
/// 3. Recursive permission YES: WriteLock -> WriteLock, WriteLock -> ReadLock. But ReadLock -> WriteLock is impossible and doesn't make sense.
/// </summary>
class Lock // ReadWriterLock Implementation
{
const int EMPTY_FLAG = 0x00000000;
const int WRITE_MASK = 0x7FFF0000;
const int READ_MASK = 0x0000FFFF;
const int MAX_SPIN_COUNT = 5000;
// Using an int for tracking current thread for recursive locking.
private int _flag;
private int writeCount = 0; // This doesn't need to be included in the flag.
public void WriteLock()
{
// Check if the same thread already holds the WriteLock.
int lockThreadId = (_flag & WRITE_MASK) >> 16;
if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
{
writeCount++;
return;
}
// Attempt to acquire the WriteLock.
while (true)
{
int desired = (Thread.CurrentThread.ManagedThreadId << 16) & WRITE_MASK;
for (int i = 0; i < MAX_SPIN_COUNT; i++)
{
if (Interlocked.CompareExchange(ref _flag, desired, EMPTY_FLAG) == EMPTY_FLAG)
{
writeCount = 1;
return;
}
}
Thread.Yield();
}
}
public void WriteUnlock()
{
int lockCount = --writeCount;
if (lockCount == 0) Interlocked.Exchange(ref _flag, EMPTY_FLAG);
}
public void ReadLock()
{
// Check if the same thread already holds the WriteLock.
int lockThreadId = (_flag & WRITE_MASK) >> 16;
if (Thread.CurrentThread.ManagedThreadId == lockThreadId)
{
Interlocked.Increment(ref _flag);
return;
}
// Attempt to acquire the ReadLock.
while (true)
{
for (int i = 0; i < MAX_SPIN_COUNT; i++)
{
int expected = (_flag & READ_MASK);
if (Interlocked.CompareExchange(ref _flag, expected + 1, expected) == expected)
return;
}
Thread.Yield();
}
}
public void ReadUnlock()
{
Interlocked.Decrement(ref _flag);
}
}
class Program
{
static void Main(string[] args)
{
// Example usage
Lock rwLock = new Lock();
Task writer = Task.Run(() =>
{
rwLock.WriteLock();
Console.WriteLine("WriteLock acquired");
Thread.Sleep(1000); // Simulate work
rwLock.WriteUnlock();
Console.WriteLine("WriteLock released");
});
Task reader = Task.Run(() =>
{
rwLock.ReadLock();
Console.WriteLine("ReadLock acquired");
Thread.Sleep(500); // Simulate work
rwLock.ReadUnlock();
Console.WriteLine("ReadLock released");
});
Task.WaitAll(writer, reader);
}
}
}