ReaderWriterLock, my naive implementation
Ron challenged me to write my own implementation of reader/writers lock. He did a great job of doing his own implementation.
I came up with this:
(note: this is really naive implementation, it’s far from ideal in terms of fairness and possible racing conditions. Just take it as brain-teaser)
1: public class StateReaderWriterLock : IDisposable2: {
3: private readonly AutoResetEvent _changeStateAutoLock = new AutoResetEvent(false);4: private readonly AutoResetEvent _writerDone = new AutoResetEvent(false);5: private readonly object _readersLock = new object();6: private readonly object _writersLock = new object();7: private int _readers;8: private int _state; // 0 is "neutral", 1 is read, 2 is write9: private int _writers;10:
11: public void LockReader()12: {
13: while (true)14: {
15: // try to bring the state from "neutral" or "read" to "read". If the current state is "write", let's wait.16: while (Interlocked.CompareExchange(ref _state, 1, 0) == 2)17: _changeStateAutoLock.WaitOne();
18:
19: // an interesting case here where the last reader is now in ReleaseRead and we're trying to read as well, we might be too late (the writer might have changed the _state already)20: // if that happens - we're back to square one, but we still want to avoid recursive locking!21: bool loseInRace = false;22: lock (_readersLock)23: {
24: if (Interlocked.CompareExchange(ref _state, 1, 0) == 2)25: loseInRace = true;26: else27: _readers++;
28: }
29: if (!loseInRace)30: return; // success!31: }
32: }
33:
34: public void ReleaseReader()35: {
36: lock (_readersLock)37: {
38: _readers--;
39:
40: // if I am the last reader, let's reset the state so any given reader/writer can take it41: if (_readers == 0)42: {
43: Thread.VolatileWrite(ref _state, 0);44: _changeStateAutoLock.Set();
45: }
46: }
47: }
48:
49: public void LockWriter()50: {
51: while (true)52: {
53: // try to bring the state from "neutral" or "write" to "write". If the current state is "read", let's wait.54: while (Interlocked.CompareExchange(ref _state, 2, 0) == 1)55: _changeStateAutoLock.WaitOne();
56:
57: bool loseInRace = false;58: lock (_writersLock)59: {
60: if (Interlocked.CompareExchange(ref _state, 2, 0) == 1)61: loseInRace = true;62: else63: _writers++;
64: }
65:
66: if (!loseInRace)67: break; // great success!68: }
69:
70: // allow 1 writer only71: if (Thread.VolatileRead(ref _writers) > 1)72: {
73: _writerDone.WaitOne();
74: }
75: }
76:
77: public void ReleaseWriter()78: {
79: lock (_writersLock)80: {
81: _writers--;
82:
83: // if I am the last writer, let's reset the state so any given reader/writer can take it84: if (_writers == 0)85: {
86: Thread.VolatileWrite(ref _state, 0);87: _changeStateAutoLock.Set();
88: }
89: else90: {
91: _writerDone.Set();
92: }
93: }
94: }
95:
96: public void Dispose()97: {
98: _changeStateAutoLock.Close();
99: _writerDone.Close();
100: }
101: }
102:
Not sure it’s the greatest job interview question, but it is indeed challenging and fun to play with.