Final code version for 2009 Advent Calendar
This is the final version of all code created in the 2009 Advent Calendar:
1: public class MutexWrapper
2: {
3: private readonly Mutex _lock = new Mutex();
4:
5: public virtual void WaitOne()
6: {
7: _lock.WaitOne();
8: }
9:
10: public virtual void ReleaseMutex()
11: {
12: _lock.ReleaseMutex();
13: }
14: }
15:
16: public interface Lock
17: {
18: void Lock();
19: void Unlock();
20: }
21:
22: public class MutexLock<T> : Lock where T : MutexWrapper, new()
23: {
24: private readonly T _lock = new T();
25:
26: public void Lock()
27: {
28: try
29: {
30: _lock.WaitOne();
31: }
32: catch (AbandonedMutexException) { }
33: }
34:
35: public void Unlock()
36: {
37: _lock.ReleaseMutex();
38: }
39: }
40:
41: public interface ImportantInterface
42: {
43: void ImportantMethod();
44: }
45:
46: public class ImportantObject : ImportantInterface
47: {
48: public void ImportantMethod()
49: {
50: // Do things.
51: }
52: }
53:
54: public class Transaction : IDisposable
55: {
56: private readonly Lock _lock;
57: public ImportantInterface ImportantObject { get; private set; }
58:
59: public Transaction(ImportantInterface importantObject, Lock aLock)
60: {
61: ImportantObject = importantObject;
62: _lock = aLock;
63: _lock.Lock();
64: }
65:
66: public void Dispose()
67: {
68: _lock.Unlock();
69: }
70: }
71:
72: public class ImportantProvider<T> where T : ImportantInterface, new()
73: {
74: private T _importantObject = new T();
75: private Lock _lock;
76:
77: public ImportantProvider()
78: : this(new MutexLock<MutexWrapper>())
79: {
80:
81: }
82:
83: public ImportantProvider(Lock aLock)
84: {
85: _lock = aLock;
86: }
87:
88: public Transaction Transaction
89: {
90: get
91: {
92: return new Transaction(_importantObject, _lock);
93: }
94: }
95: }
96:
97: public class Given_an_unlocked_MutexWrapper
98: {
99: private MutexWrapper _lock = new MutexWrapper();
100:
101: [Fact(Timeout = 1000)]
102: void It_should_be_possible_to_WaitOne()
103: {
104: _lock.WaitOne();
105: }
106: }
107:
108: public class Given_a_locked_MutexWrapper : IDisposable
109: {
110: private MutexWrapper _lock = new MutexWrapper();
111: private Thread _thread;
112: private bool _gotLock = false;
113:
114: public Given_a_locked_MutexWrapper()
115: {
116: _thread = new Thread(() =>
117: {
118: _lock.WaitOne();
119: _gotLock = true;
120: _lock.ReleaseMutex();
121: });
122: }
123:
124: public void Dispose()
125: {
126: if (_thread != null)
127: {
128: _thread.Abort();
129: }
130: }
131:
132: private void CompleteSetup()
133: {
134: _lock.WaitOne();
135: _thread.Start();
136: Assert.False(_thread.Join(250));
137: }
138:
139: [Fact(Timeout = 1000)]
140: void It_should_block_on_WaitOne()
141: {
142: CompleteSetup();
143: Assert.False(_gotLock);
144: }
145:
146: [Fact(Timeout = 1000)]
147: void It_should_complete_WaitOne_once_released()
148: {
149: CompleteSetup();
150: _lock.ReleaseMutex();
151: Assert.True(_thread.Join(500));
152: Assert.True(_gotLock);
153: }
154: }
155:
156: public class Given_an_abandoned_MutexWrapper : IDisposable
157: {
158: private MutexWrapper _lock = new MutexWrapper();
159: private EventWaitHandle _threadStarted = new EventWaitHandle(false, EventResetMode.ManualReset);
160: private EventWaitHandle _threadStop = new EventWaitHandle(false, EventResetMode.ManualReset);
161: private Thread _thread;
162:
163: public Given_an_abandoned_MutexWrapper()
164: {
165: _thread = new Thread(() =>
166: {
167: _lock.WaitOne();
168: _threadStarted.Set();
169: _threadStop.WaitOne();
170: });
171: _thread.Start();
172: }
173:
174: public void Dispose()
175: {
176: if (_thread != null)
177: {
178: _thread.Abort();
179: }
180: }
181:
182: [Fact(Timeout = 1000)]
183: void It_should_throw_exception_when_waited_for()
184: {
185: _threadStarted.WaitOne();
186: _threadStop.Set();
187: Assert.Throws<AbandonedMutexException>(() => { _lock.WaitOne(); });
188: }
189: }
190:
191: public class Given_an_unlocked_MutexLock
192: {
193: private class MutexWrapperAlwaysUnlocked : MutexWrapper
194: {
195: public static int NumberOfLocks { get; set; }
196:
197: public override void WaitOne()
198: {
199: ++NumberOfLocks;
200: }
201:
202: public override void ReleaseMutex()
203: {
204:
205: }
206: }
207:
208: private MutexLock<MutexWrapperAlwaysUnlocked> _lock = new MutexLock<MutexWrapperAlwaysUnlocked>();
209:
210: public Given_an_unlocked_MutexLock()
211: {
212: MutexWrapperAlwaysUnlocked.NumberOfLocks = 0;
213: }
214:
215: [Fact]
216: void It_should_be_possible_to_lock()
217: {
218: _lock.Lock();
219: Assert.Equal(1, MutexWrapperAlwaysUnlocked.NumberOfLocks);
220: }
221: }
222:
223: public class Given_a_locked_MutexLock
224: {
225: private class FakeMutexWrapper : MutexWrapper
226: {
227: public bool Locked { get; private set; }
228:
229: public static int NumberOfLocks { get; private set; }
230: public static int NumberOfUnlocks { get; private set; }
231:
232: public static void Reset()
233: {
234: NumberOfLocks = 0;
235: NumberOfUnlocks = 0;
236: }
237:
238: public FakeMutexWrapper()
239: {
240: Locked = false;
241: }
242:
243: public override void WaitOne()
244: {
245: if (!Locked)
246: {
247: Locked = true;
248: ++NumberOfLocks;
249: }
250: }
251:
252: public override void ReleaseMutex()
253: {
254: Locked = false;
255: ++NumberOfUnlocks;
256: }
257: }
258:
259: private MutexLock<FakeMutexWrapper> _lock = new MutexLock<FakeMutexWrapper>();
260:
261: public Given_a_locked_MutexLock()
262: {
263: _lock.Lock();
264: FakeMutexWrapper.Reset();
265: }
266:
267: [Fact]
268: void It_should_not_take_the_lock()
269: {
270: _lock.Lock();
271: Assert.Equal(0, FakeMutexWrapper.NumberOfLocks);
272: }
273:
274: [Fact]
275: void It_should_take_lock_when_released()
276: {
277: _lock.Unlock();
278: _lock.Lock();
279: Assert.Equal(1, FakeMutexWrapper.NumberOfLocks);
280: }
281: }
282:
283: public class Given_an_abandoned_lock
284: {
285: private class MutexWrapperAlwaysAbandoned : MutexWrapper
286: {
287: public new void WaitOne()
288: {
289: throw new AbandonedMutexException();
290: }
291: }
292:
293: private MutexLock<MutexWrapperAlwaysAbandoned> _lock;
294:
295: public Given_an_abandoned_lock()
296: {
297: _lock = new MutexLock<MutexWrapperAlwaysAbandoned>();
298: }
299:
300: [Fact]
301: void It_should_be_possible_to_take_lock_when_thread_dies()
302: {
303: Assert.DoesNotThrow(() => { _lock.Lock(); });
304: }
305: }
306:
307: class FakeLock : Lock
308: {
309: public bool IsLocked { get; private set; }
310: public int NumberOfLocks { get; private set; }
311:
312: public FakeLock()
313: {
314: IsLocked = false;
315: NumberOfLocks = 0;
316: }
317:
318: public void Lock()
319: {
320: IsLocked = true;
321: ++NumberOfLocks;
322: }
323:
324: public void Unlock()
325: {
326: IsLocked = false;
327: }
328: }
329:
330: class DummyObject : ImportantInterface
331: {
332: public void ImportantMethod()
333: {
334: Assert.True(false, "Dummy should never be used");
335: }
336: }
337:
338: public class When_using_a_transaction
339: {
340: private FakeLock _lock;
341:
342: public When_using_a_transaction()
343: {
344: _lock = new FakeLock();
345: }
346:
347: [Fact]
348: void It_should_take_lock_when_created()
349: {
350: Assert.False(_lock.IsLocked);
351: using (Transaction transaction = new Transaction(new ImportantObject(), _lock))
352: {
353: Assert.True(_lock.IsLocked);
354: }
355: }
356:
357: [Fact]
358: void It_should_release_lock_when_leaving_scope()
359: {
360: using (Transaction transaction = new Transaction(new ImportantObject(), _lock))
361: {
362: Assert.True(_lock.IsLocked);
363: }
364: Assert.False(_lock.IsLocked);
365: }
366: }
367:
368: public class Given_a_transaction
369: {
370: private Transaction _transaction = new Transaction(new DummyObject(), new FakeLock());
371:
372: [Fact]
373: void It_should_return_an_ImportantObject()
374: {
375: Assert.NotNull(_transaction.ImportantObject);
376: }
377: }
378:
379: public class Given_an_ImportantProvider
380: {
381: private ImportantProvider<DummyObject> _importantProvider = new ImportantProvider<DummyObject>();
382:
383: [Fact]
384: void It_should_return_a_transaction()
385: {
386: Assert.NotNull(_importantProvider.Transaction);
387: }
388: }
389:
390: public class Given_two_transactions_from_the_same_ImportantProvider
391: {
392: private FakeLock _lock;
393: private ImportantProvider<DummyObject> _importantProvider;
394: private Transaction _transaction1;
395: private Transaction _transaction2;
396:
397: public Given_two_transactions_from_the_same_ImportantProvider()
398: {
399: _lock = new FakeLock();
400: _importantProvider = new ImportantProvider<DummyObject>(_lock);
401: _transaction1 = _importantProvider.Transaction;
402: _transaction2 = _importantProvider.Transaction;
403: }
404:
405: [Fact]
406: void It_should_be_two_different_transactions()
407: {
408: Assert.NotSame(_transaction1, _transaction2);
409: }
410:
411: [Fact]
412: void It_should_be_the_same_ImportantObject_returned_by_both_transactions()
413: {
414: Assert.Same(_transaction1.ImportantObject, _transaction2.ImportantObject);
415: }
416:
417: [Fact]
418: void It_should_take_lock_once_for_each_transaction()
419: {
420: Assert.Equal(2, _lock.NumberOfLocks);
421: }
422: }