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 : IDisposable 2: { 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 write 9: 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: else 27: _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 it 41: 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: else 63: _writers++; 64: } 65: 66: if (!loseInRace) 67: break; // great success! 68: } 69: 70: // allow 1 writer only 71: 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 it 84: if (_writers == 0) 85: { 86: Thread.VolatileWrite(ref _state, 0); 87: _changeStateAutoLock.Set(); 88: } 89: else 90: { 91: _writerDone.Set(); 92: } 93: } 94: } 95: 96: public void Dispose() 97: { 98: _changeStateAutoLock.Close(); 99: _writerDone.Close(); 100: } 101: } 102:
1: public class StateReaderWriterLock : IDisposable
2: {
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 write
9: 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: else
27: _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 it
41: 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: else
63: _writers++;
64: }
65:
66: if (!loseInRace)
67: break; // great success!
68: }
69:
70: // allow 1 writer only
71: 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 it
84: if (_writers == 0)
85: {
86: Thread.VolatileWrite(ref _state, 0);
87: _changeStateAutoLock.Set();
88: }
89: else
90: {
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.
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2010, Oren Ellenbogen
<= Contact me via E-mail
newtelligence dasBlog 2.2.8279.16125