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: }