/// <summary> /// Release any resources associated with this object. /// </summary> public void Dispose() { using (TimedLock.Lock(this)) { #if DEBUG && !MOBILE_DEVICE if (!isCompleted) { AsyncTracker.Dump(this, "Disposing AsyncResult before operation completes."); SysLog.LogErrorStackDump("Disposing AsyncResult before operation completes."); return; } if (disposed) { var sb = new StringBuilder(1024); AsyncTracker.Dump(this, "AsyncResult has already been disposed."); sb.AppendLine("AsyncResult has already been disposed."); sb.AppendLine(); sb.AppendLine("Current stack:"); CallStack.AsyncTrace(1, true).Dump(sb); sb.AppendLine(); sb.AppendLine("Completion stack:"); completeStack.Dump(sb); SysLog.LogError(sb.ToString()); return; } this.disposed = true; this.completeStack = CallStack.AsyncTrace(1, true); #endif if (wait != null) { wait.Close(); wait = null; } } #if !MOBILE_DEVICE AsyncTracker.Yield(); #endif }
public override bool WaitOne() { using (TimedLock.Lock(owner)) { if (owner.WaitStack == null) { owner.WaitStack = CallStack.AsyncTrace(0, true); } if (isWaiting) { throw new InvalidOperationException("Another thread is already waiting."); } isWaiting = true; } return(wait.WaitOne()); }
public override bool WaitOne(int millisecondTimeout, bool exitContext) { using (TimedLock.Lock(owner)) { if (owner.WaitStack == null) { owner.WaitStack = CallStack.AsyncTrace(0, true); } if (!isWaiting) { throw new InvalidOperationException("Another thread is already waiting."); } isWaiting = true; } return(wait.WaitOne(millisecondTimeout, exitContext)); }
public void Started(bool traceEnable) { #if DEBUG && !MOBILE_DEVICE if (traceEnable) { // The trace name will be the name of the object type and method // that called this method. var stack = new CallStack(1, false); var method = stack.GetFrame(0).GetMethod(); this.traceName = method.DeclaringType.Name + "." + method.Name; this.traceID = AsyncTraceID.GetNextID(); } Trace("Started()"); this.startStack = CallStack.AsyncTrace(1, true); AsyncTracker.Add(this); AsyncTracker.Yield(); #endif }
/// <summary> /// Adds additional information about the current thread (the thread that /// could not acquire the lock) and other locks held by the current thread and the /// thread that did acquire the lock to the <see cref="LockInfo" /> instance /// so that errors can be reported. /// </summary> /// <param name="info">The source lock information.</param> /// <param name="curStack">The current call stack.</param> /// <returns>A cloned version of the source with the additonal information.</returns> private LockInfo GetDiagnosticInfo(LockInfo info, CallStack curStack) { info = info.Clone(); info.FailNativeThreadID = GetCurrentThreadId(); info.FailManagedThreadID = Thread.CurrentThread.ManagedThreadId; info.FailStack = curStack; info.Locks = new List <LockInfo>(); info.FailLocks = new List <LockInfo>(); foreach (LockInfo l in locks.Values) { if (l.ManagedThreadID == info.ManagedThreadID) { info.Locks.Add(l); } else if (l.ManagedThreadID == info.FailManagedThreadID) { info.FailLocks.Add(l); } } return(info); }
/// <summary> /// Notify the application that the operation is complete /// via the call back delegate. /// </summary> /// <param name="e"> /// The exception to be thrown in the <see cref="Finish" /> method /// or <c>null</c> if the operation was completed successfully. /// </param> /// <remarks> /// <note> /// Although this method may be called more than once for /// a particular async result instance, all but the first call /// will be ignored. /// </note> /// </remarks> public void Notify(Exception e) { #if DEBUG && !MOBILE_DEVICE // Wait up to a second to verify that Started() has been // called before reporting a warning. DateTime endTime = SysTime.Now + TimeSpan.FromSeconds(1); bool started = this.startStack != null; while (!started && SysTime.Now < endTime) { Thread.Sleep(10); started = this.startStack != null; } if (!started) { Trace("Notify({0}): Warning: Started() not called", exception != null ? exception.GetType().Name : (result == null ? "null" : result.ToString())); AsyncTracker.Dump(this, "Warning: Started() not called before Notifiy()"); } if (notifyQueued) { Trace("Notify({0}: Already queued)", exception != null ? exception.GetType().Name : (result == null ? "null" : result.ToString())); return; } Trace("Notify({0})", exception != null ? exception.GetType().Name : (result == null ? "null" : result.ToString())); notifyStack = CallStack.AsyncTrace(1, true); if (NotifyHook != null) { NotifyHook(this, e); } #endif #if !MOBILE_DEVICE AsyncTracker.Yield(); #endif using (TimedLock.Lock(this)) { notifyQueued = true; if (isCompleted) { return; // We've already signaled completion } exception = e; if (callback != null) { if (syncCompletion) { callback(this); } else { Helper.UnsafeQueueUserWorkItem(onComplete, this); } } else { isCompleted = true; if (wait != null) { wait.Set(); } } } #if !MOBILE_DEVICE AsyncTracker.Yield(); #endif }
/// <summary> /// Attempts to obtain a lock on an object with the specified timeout. /// </summary> /// <param name="target">The object whose lock is to be acquired.</param> /// <param name="timeout">The maximum time to wait.</param> private void _Lock(object target, TimeSpan timeout) { if (fullDiagnostics && lockableWarning && target as ILockable == null) { string typeName = target.GetType().FullName; bool warn; lock (locks) { warn = !prevWarnings.ContainsKey(typeName); if (warn) { prevWarnings.Add(typeName, true); } } if (warn) { SysLog.LogWarning("Type [{0}] does not implement [ILockable] at:\r\n\r\n" + new CallStack(2, true).ToString(), typeName); } } #if !WINFULL // There appears to be some problems with [Monitor.TryEnter()] on Windows Phone and possibly // also on Silverlight where exclusive access cannot be obtained sometimes when there // are no other locks. Simply replacing [TryEnter()] with [Enter()] fixes the problem and // since we were using [TryEnter()] only for enhanced diagnostics, removing this will // really doesn't change the programming model. The only difference will be that WINFULL // builds will throw a [DeadlockException] when the lock timeout has been exceeded whereas // SILVERLIGHT and MOBILE_DEVICE builds will simply block forever in [Enter()] // for deadlocks. Monitor.Enter(target); #else if (!Monitor.TryEnter(target, timeout)) { var failStack = new CallStack(2, true); #if LEAK_DETECTOR leakDetector.SuppressFinalize(); #endif if (fullDiagnostics) { var lockable = target as ILockable; if (lockable == null) { DumpAndBreak(target, null); throw new DeadlockException(this, failStack, null, "#1"); } lock (locks) { LockInfo info; if (locks.TryGetValue(lockable.GetLockKey(), out info)) { var diagnostics = GetDiagnosticInfo(info, failStack); DumpAndBreak(target, diagnostics); throw new DeadlockException(this, failStack, diagnostics, "#2"); } else { DumpAndBreak(target, null); throw new DeadlockException(this, failStack, null, "#3"); } } } else { DumpAndBreak(target, null); throw new DeadlockException(this, failStack, null, "#4"); } } else { #if DEBUG var lockTime = DateTime.MinValue; if (excessiveHoldTime > 0) { lockTime = SysTime.Now; } #endif if (fullDiagnostics) { lock (locks) { var lockable = target as ILockable; if (lockable != null) { object lockKey = lockable.GetLockKey(); LockInfo info; if (!locks.TryGetValue(lockKey, out info)) { locks.Add(lockKey, new LockInfo(target, new CallStack(2, true))); } else { info.LockCount++; } } } } #if DEBUG if (excessiveHoldTime > 0 && SysTime.Now - lockTime >= TimeSpan.FromMilliseconds(excessiveHoldTime)) { SysLog.LogErrorStackDump("Warning: Lock held for an excessive period of time: {0}ms", (SysTime.Now - lockTime).TotalMilliseconds); } #endif } #endif // WINFULL }