protected virtual void Initialize(Action beforeInitialize) { if (beforeInitialize == null) { throw new ArgumentNullException("beforeInitialize"); } if (ThreadingHelper.VolatileRead(ref _valueFactory) == null) { // If unable to initialize do nothing // This happens if // - initialization is done // - ReleaseValueFactory was called // Even if ReleaseValueFactory was called before initialization, // _target can still be set by SetTargetValue or the Value property return; } try { beforeInitialize.Invoke(); } finally { InitializeExtracted(); } }
private ManualResetEvent TryGetWaitHandleExtracted() { var handle = ThreadingHelper.VolatileRead(ref _handle); if (handle != null) { if (Thread.VolatileRead(ref _requested) == 2) { return(handle); } handle.Close(); } Thread.VolatileWrite(ref _requested, -1); throw new ObjectDisposedException(GetType().FullName); }
internal void FinishThreadAbortedTask(bool exceptionAdded, bool delegateRan) { if (Interlocked.CompareExchange(ref _threadAbortedmanaged, 1, 0) == 0) { var exceptionsHolder = ThreadingHelper.VolatileRead(ref _exceptionsHolder); if (exceptionsHolder == null) { return; } if (exceptionAdded) { exceptionsHolder.MarkAsHandled(false); } Finish(delegateRan); } }
/// <summary> /// This is to be called just before the task does its final state transition. /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list /// </summary> internal void AddExceptionsFromChildren() { // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren // being nulled out while it is being processed, which could lead to a NullReferenceException. To // protect ourselves, we'll cache m_exceptionalChildren in a local variable. var tmp = ThreadingHelper.VolatileRead(ref _exceptionalChildren); if (tmp != null) { // This lock is necessary because even though AddExceptionsFromChildren is last to execute, it may still // be racing with the code segment at the bottom of Finish() that prunes the exceptional child array. lock (tmp) { foreach (var task in tmp) { // Ensure any exceptions thrown by children are added to the parent. // In doing this, we are implicitly marking children as being "handled". Contract.Assert(task.IsCompleted, "Expected all tasks in list to be completed"); if (task.IsFaulted && !task.IsExceptionObservedByParent) { var exceptionsHolder = ThreadingHelper.VolatileRead(ref task._exceptionsHolder); if (exceptionsHolder == null) { Contract.Assert(false); } else { // No locking necessary since child task is finished adding exceptions // and concurrent CreateExceptionObject() calls do not constitute // a concurrency hazard. AddException(exceptionsHolder.CreateExceptionObject(false, null)); } } } } // Reduce memory pressure by getting rid of the array ThreadingHelper.VolatileWrite(ref _exceptionalChildren, null); } }
internal void Finish(bool userDelegateExecuted) { if (!userDelegateExecuted) { // delegate didn't execute => no children. We can safely call the remaining finish stages FinishStageTwo(); } else { // Reaching this sub clause means there may be remaining active children, // and we could be racing with one of them to call FinishStageTwo(). // So whoever does the final Interlocked.Dec is responsible to finish. if ((_completionCountdown == 1 && !IsSelfReplicatingRoot) || Interlocked.Decrement(ref _completionCountdown) == 0) { FinishStageTwo(); } else { // Apparently some children still remain. It will be up to the last one to process the completion of this task on their own thread. // We will now yield the thread back to ThreadPool. Mark our state appropriately before getting out. // We have to use an atomic update for this and make sure not to overwrite a final state, // because at this very moment the last child's thread may be concurrently completing us. // Otherwise we risk overwriting the TaskStatus which may have been set by that child task. Interlocked.CompareExchange(ref _status, (int)TaskStatus.WaitingForChildrenToComplete, (int)TaskStatus.Running); } // Now is the time to prune exceptional children. We'll walk the list and removes the ones whose exceptions we might have observed after they threw. // we use a local variable for exceptional children here because some other thread may be nulling out _exceptionalChildren var exceptionalChildren = ThreadingHelper.VolatileRead(ref _exceptionalChildren); if (exceptionalChildren != null) { lock (exceptionalChildren) { exceptionalChildren.RemoveAll(_isExceptionObservedByParentPredicate); // RemoveAll has better performance than doing it ourselves } } } }
internal void ProcessChildCompletion(Task childTask) { Contract.Requires(childTask != null); Contract.Requires(childTask.IsCompleted, "ProcessChildCompletion was called for an uncompleted task"); Contract.Assert(childTask._parent == this, "ProcessChildCompletion should only be called for a child of this task"); // if the child threw and we haven't observed it we need to save it for future reference if (childTask.IsFaulted && !childTask.IsExceptionObservedByParent) { // Lazily initialize the child exception list if (ThreadingHelper.VolatileRead(ref _exceptionalChildren) == null) { Interlocked.CompareExchange(ref _exceptionalChildren, new List <Task>(), null); } // In rare situations involving AppDomainUnload, it's possible (though unlikely) for FinishStageTwo() to be called // multiple times for the same task. In that case, AddExceptionsFromChildren() could be nulling m_exceptionalChildren // out at the same time that we're processing it, resulting in a NullReferenceException here. We'll protect // ourselves by caching m_exceptionChildren in a local variable. var tmp = ThreadingHelper.VolatileRead(ref _exceptionalChildren); if (tmp != null) { lock (tmp) { tmp.Add(childTask); } } } if (Interlocked.Decrement(ref _completionCountdown) == 0) { // This call came from the final child to complete, and apparently we have previously given up this task's right to complete itself. // So we need to invoke the final finish stage. FinishStageTwo(); } }
public static T Read <T>(ref T location) where T : class { return(ThreadingHelper.VolatileRead(ref location)); }
/// <summary> /// Returns a list of exceptions by aggregating the holder's contents. Or null if /// no exceptions have been thrown. /// </summary> /// <param name="includeTaskCanceledExceptions">Whether to include a TCE if cancelled.</param> /// <returns>An aggregate exception, or null if no exceptions have been caught.</returns> private AggregateException GetExceptions(bool includeTaskCanceledExceptions) { // // WARNING: The Task/Task<TResult>/TaskCompletionSource classes // have all been carefully crafted to insure that GetExceptions() // is never called while AddException() is being called. There // are locks taken on m_contingentProperties in several places: // // -- Task<TResult>.TrySetException(): The lock allows the // task to be set to Faulted state, and all exceptions to // be recorded, in one atomic action. // // -- Task.Exception_get(): The lock ensures that Task<TResult>.TrySetException() // is allowed to complete its operation before Task.Exception_get() // can access GetExceptions(). // // -- Task.ThrowIfExceptional(): The lock insures that Wait() will // not attempt to call GetExceptions() while Task<TResult>.TrySetException() // is in the process of calling AddException(). // // For "regular" tasks, we effectively keep AddException() and GetException() // from being called concurrently by the way that the state flows. Until // a Task is marked Faulted, Task.Exception_get() returns null. And // a Task is not marked Faulted until it and all of its children have // completed, which means that all exceptions have been recorded. // // It might be a lot easier to follow all of this if we just required // that all calls to GetExceptions() and AddExceptions() were made // under a lock on m_contingentProperties. But that would also // increase our lock occupancy time and the frequency with which we // would need to take the lock. // // If you add a call to GetExceptions() anywhere in the code, // please continue to maintain the invariant that it can't be // called when AddException() is being called. // // We'll lazily create a TCE if the task has been canceled. Exception canceledException = null; if (includeTaskCanceledExceptions && IsCanceled) { // Backcompat: // Ideally we'd just use the cached OCE from this.GetCancellationExceptionDispatchInfo() // here. However, that would result in a potentially breaking change from .NET 4, which // has the code here that throws a new exception instead of the original, and the EDI // may not contain a TCE, but an OCE or any OCE-derived type, which would mean we'd be // propagating an exception of a different type. canceledException = new TaskCanceledException(this); } var exceptionsHolder = ThreadingHelper.VolatileRead(ref _exceptionsHolder); if (exceptionsHolder != null && exceptionsHolder.ContainsFaultList) { // No need to lock around this, as other logic prevents the consumption of exceptions // before they have been completely processed. return(_exceptionsHolder.CreateExceptionObject(false, canceledException)); } if (canceledException != null) { // No exceptions, but there was a cancelation. Aggregate and return it. return(new AggregateException(canceledException)); } return(null); }
private void InitializeExtracted() { back: var valueFactory = Interlocked.Exchange(ref _valueFactory, null); if (valueFactory == null) { // Many threads may enter here // Prevent reentry if (_initializerThread == Thread.CurrentThread) { throw new InvalidOperationException(); } var waitHandle = _waitHandle.Value; // While _waitHandle.Value is not null it means that we have to wait initialization to complete if (waitHandle != null) { try { // Another thread may have called ReleaseWaitHandle just before the next instruction waitHandle.Wait(); // Finished waiting... if (ThreadingHelper.VolatileRead(ref _valueFactory) != null) { // There was an error in the initialization, go back goto back; } ReleaseWaitHandle(); } catch (ObjectDisposedException exception) { // Came late to the party, initialization is done GC.KeepAlive(exception); } } } else { // Only one thread enters here _initializerThread = Thread.CurrentThread; try { // Initialize from the value factory SetTargetValue(valueFactory.Invoke()); // Initialization done, let any wating thread go ReleaseWaitHandle(); } catch (Exception exception) { // There was an error during initialization // Set error state SetTargetError(exception); // Restore the valueFactory Interlocked.CompareExchange(ref _valueFactory, valueFactory, null); // Let any waiting threads go, but don't get rid of the wait handle _waitHandle.Value.Set(); throw; } finally { _initializerThread = null; } } }