1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
using System;
using System.Threading;
// Thanks to Eric Gunnerson for recommending this be a struct rather
// than a class -- avoids a heap allocation.
// Thanks to Change Gillespie and Jocelyn Coulmance for pointing out
// the bugs that then crept in when I changed it to use struct...
// Thanks to John Sands for providing the necessary incentive to make
// me invent a way of using a struct in both release and debug builds
// without losing the debug leak tracking.
public struct TimedLock : IDisposable
{
public static TimedLock Lock (object o)
{
return Lock (o, TimeSpan.FromSeconds (10));
}
public static TimedLock Lock (object o, TimeSpan timeout)
{
TimedLock tl = new TimedLock (o);
if (!Monitor.TryEnter (o, timeout))
{
#if DEBUG
System.GC.SuppressFinalize(tl.leakDetector);
#endif
throw new LockTimeoutException ();
}
return tl;
}
private TimedLock (object o)
{
target = o;
#if DEBUG
leakDetector = new Sentinel();
#endif
}
private object target;
public void Dispose ()
{
Monitor.Exit (target);
// It's a bad error if someone forgets to call Dispose,
// so in Debug builds, we put a finalizer in to detect
// the error. If Dispose is called, we suppress the
// finalizer.
#if DEBUG
GC.SuppressFinalize(leakDetector);
#endif
}
#if DEBUG
// (In Debug mode, we make it a class so that we can add a finalizer
// in order to detect when the object is not freed.)
private class Sentinel
{
~Sentinel()
{
// If this finalizer runs, someone somewhere failed to
// call Dispose, which means we've failed to leave
// a monitor!
System.Diagnostics.Debug.Fail("Undisposed lock");
}
}
private Sentinel leakDetector;
#endif
}
public class LockTimeoutException : ApplicationException
{
public LockTimeoutException () : base("Timeout waiting for lock")
{
}
}
|