Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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: }