Share via


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