public void InitialNoInnerLocks() { object monitor = new object(); DateTime start = DateTime.UtcNow; for (int i=0; i < Iterations; i++) { lock (monitor) { } } DateTime end = DateTime.UtcNow; TimeSpan nativeTime = end-start; OrderedLock syncLock = new OrderedLock(Timeout.Infinite); start = DateTime.UtcNow; for (int i=0; i < Iterations; i++) { using (syncLock.Lock()) { } } end = DateTime.UtcNow; TimeSpan syncLockTime = end-start; double factor = syncLockTime.TotalMilliseconds / nativeTime.TotalMilliseconds; Console.WriteLine ("Performance with no inner locks (initial acquisition):"); Console.WriteLine ("Native: {0}", nativeTime); Console.WriteLine ("OrderedLock: {0}", syncLockTime); Console.WriteLine ("Performance penalty factor: {0:0.00}", factor); Console.WriteLine(); Assert.IsTrue (factor < 10, "OrderedLock must not be ridiculously slow"); }
public void Setup() { subject = new OrderedLock("Test", Timeout.Infinite); inner1.InnerLock = inner2; subject.InnerLock = inner1; }
/// <summary> /// Locks the monitor, with the specified timeout. This implementation validates /// the ordering of locks, and maintains the current owner. /// </summary> /// <param name="timeout">The timeout, in milliseconds. Must be Timeout.Infinite, /// or non-negative.</param> /// <returns>A lock token which should be disposed to release the lock</returns> /// <exception cref="LockTimeoutException">The operation times out.</exception> /// <exception cref="LockOrderException"> /// The lock order would be violated if this lock were taken out. (i.e. attempting /// to acquire the lock could cause deadlock.) /// </exception> public override LockToken Lock(int timeout) { // Check whether we should be allowed to take out this lock, according to // the inner locks we have. // Performance note: This would be in a separate method, but the cost of // making a method call (which can't be inlined in this case) is sufficiently // high as to make it worth manually inlining. OrderedLock inner = InnerLock; // Performance note: This would be a single if statement with shortcutting, // but fetching the current thread is mildly expensive. if (inner != null) { Thread currentThread = Thread.CurrentThread; if (Owner != currentThread) { while (inner != null) { if (inner.Owner == currentThread) { throw new LockOrderException("Unable to acquire lock {0} as lock {1} is already held", Name, inner.Name); } inner = inner.InnerLock; } } } LockToken ret = base.Lock(timeout); // Now remember that we've locked, and set the owner if necessary // Performance note: On a single processor, it is slightly cheaper // to assign owner every time, without a test. On multiple processor // boxes, it is cheaper to avoid the volatile write. if (Interlocked.Increment(ref count) == 1) { owner = Thread.CurrentThread; } return(ret); }
public void AlreadyOwnedTwoInnerLocks() { DateTime start, end; object monitor = new object(); lock (monitor) { start = DateTime.UtcNow; for (int i=0; i < Iterations; i++) { lock (monitor) { } } end = DateTime.UtcNow; } TimeSpan nativeTime = end-start; OrderedLock orderedLock = new OrderedLock(Timeout.Infinite); OrderedLock inner1 = new OrderedLock(Timeout.Infinite); OrderedLock inner2 = new OrderedLock(Timeout.Infinite); orderedLock.InnerLock = inner1; inner1.InnerLock = inner2; using (orderedLock.Lock()) { start = DateTime.UtcNow; for (int i=0; i < Iterations; i++) { using (orderedLock.Lock()) { } } end = DateTime.UtcNow; } TimeSpan syncLockTime = end-start; double factor = syncLockTime.TotalMilliseconds / nativeTime.TotalMilliseconds; Console.WriteLine ("Performance with two inner locks (lock already owned):"); Console.WriteLine ("Native: {0}", nativeTime); Console.WriteLine ("OrderedLock: {0}", syncLockTime); Console.WriteLine ("Performance penalty factor: {0:0.00}", factor); Console.WriteLine(); Assert.IsTrue (factor < 10, "OrderedLock must not be ridiculously slow"); }
/// <summary> /// Sets the "inner" lock for this lock, returning this lock. This /// is a convenience method for setting InnerLock as part of a variable /// declaration. /// </summary> /// <example> /// OrderedLock inner = new OrderedLock(); /// OrderedLock outer = new OrderedLock().SetInnerLock(inner); /// </example> /// <param name="inner">The inner </param> /// <returns>This lock is returned.</returns> public OrderedLock SetInnerLock (OrderedLock inner) { InnerLock = inner; return this; }
/// <summary> /// Sets the "inner" lock for this lock, returning this lock. This /// is a convenience method for setting InnerLock as part of a variable /// declaration. /// </summary> /// <example> /// OrderedLock inner = new OrderedLock(); /// OrderedLock outer = new OrderedLock().SetInnerLock(inner); /// </example> /// <param name="inner">The inner </param> /// <returns>This lock is returned.</returns> public OrderedLock SetInnerLock(OrderedLock inner) { InnerLock = inner; return(this); }