/// <summary> /// Disposes of the registration and unregisters the target callback from the associated /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see>. /// If the target callback is currently executing this method will wait until it completes, except /// in the degenerate cases where a callback method deregisters itself. /// </summary> public void Dispose() { // If the token source has been disposed, we must throw. if (m_tokenSource != null) { m_tokenSource.ThrowIfDisposed(); } // Remove the entry from the array. // This call includes a full memory fence which prevents potential reorderings of the reads below bool deregisterOccured = TryDeregister(); // We guarantee that we will not return if the callback is being executed (assuming we are not currently called by the callback itself) // We achieve this by the following rules: // 1. if we are called in the context of an executing callback, no need to wait (determined by tracking callback-executor threadID) // - if the currently executing callback is this CTR, then waiting would deadlock. (We choose to return rather than deadlock) // - if not, then this CTR cannot be the one executing, hence no need to wait // // 2. if deregistration failed, and we are on a different thread, then the callback may be running under control of cts.Cancel() // => poll until cts.ExecutingCallback is not the one we are trying to deregister. if (m_tokenSource != null && m_tokenSource.IsCancellationRequested && //running callbacks has commenced. !m_tokenSource.IsCancellationCompleted && //running callbacks hasn't finished !deregisterOccured && //deregistration failed (ie the callback is missing from the list) m_tokenSource.ThreadIDExecutingCallbacks != Thread.CurrentThread.ManagedThreadId) //the executingThreadID is not this threadID. { // Callback execution is in progress, the executing thread is different to us and has taken the callback for execution // so observe and wait until this target callback is no longer the executing callback. m_tokenSource.WaitForCallbackToComplete(m_callbackInfo); } }
public void Dispose() { bool flag = this.TryDeregister(); CancellationCallbackInfo callbackInfo = this.m_callbackInfo; if (callbackInfo != null) { CancellationTokenSource cancellationTokenSource = callbackInfo.CancellationTokenSource; if (cancellationTokenSource.IsCancellationRequested && !cancellationTokenSource.IsCancellationCompleted && !flag && cancellationTokenSource.ThreadIDExecutingCallbacks != Thread.CurrentThread.ManagedThreadId) { cancellationTokenSource.WaitForCallbackToComplete(this.m_callbackInfo); } } }
public void Dispose() { bool flag = this.TryDeregister(); CancellationCallbackInfo cancellationCallbackInfo = this.m_callbackInfo; if (cancellationCallbackInfo == null) { return; } CancellationTokenSource cancellationTokenSource = cancellationCallbackInfo.CancellationTokenSource; if (!cancellationTokenSource.IsCancellationRequested || cancellationTokenSource.IsCancellationCompleted || (flag || cancellationTokenSource.ThreadIDExecutingCallbacks == Thread.CurrentThread.ManagedThreadId)) { return; } cancellationTokenSource.WaitForCallbackToComplete(this.m_callbackInfo); }
private void WaitForCallbackIfNecessary() { // We're a valid registration but we were unable to unregister, which means the callback wasn't in the list, // which means either it already executed or it's currently executing. We guarantee that we will not return // if the callback is being executed (assuming we are not currently called by the callback itself) // We achieve this by the following rules: // 1. If we are called in the context of an executing callback, no need to wait (determined by tracking callback-executor threadID) // - if the currently executing callback is this CTR, then waiting would deadlock. (We choose to return rather than deadlock) // - if not, then this CTR cannot be the one executing, hence no need to wait // 2. If unregistration failed, and we are on a different thread, then the callback may be running under control of cts.Cancel() // => poll until cts.ExecutingCallback is not the one we are trying to unregister. CancellationTokenSource source = _node.Partition.Source; if (source.IsCancellationRequested && // Running callbacks has commenced. !source.IsCancellationCompleted && // Running callbacks hasn't finished. source.ThreadIDExecutingCallbacks != Environment.CurrentManagedThreadId) // The executing thread ID is not this thread's ID. { // Callback execution is in progress, the executing thread is different from this thread and has taken the callback for execution // so observe and wait until this target callback is no longer the executing callback. source.WaitForCallbackToComplete(_id); } }