/// <summary>Ensures that the specified token matches the current version.</summary> /// <param name="token">The token supplied by <see cref="ValueTask"/>.</param> private void ValidateToken(short token) { if (token != _version) { ManualResetValueTaskSourceCoreShared.ThrowInvalidOperationException(); } }
/// <summary>Signals that that the operation has completed. Invoked after the result or error has been set.</summary> private void SignalCompletion() { if (_completed) { ManualResetValueTaskSourceCoreShared.ThrowInvalidOperationException(); } _completed = true; if (_continuation != null || Interlocked.CompareExchange(ref _continuation, ManualResetValueTaskSourceCoreShared.s_sentinel, null) != null) { if (_executionContext != null) { ExecutionContext.Run(_executionContext, s => ((ManualResetValueTaskSourceCore <TResult>)s).InvokeContinuation(), this); //ExecutionContext.RunInternal( // _executionContext, // (ref ManualResetValueTaskSourceCore<TResult> s) => s.InvokeContinuation(), // ref this); } else { InvokeContinuation(); } } }
public TResult GetResult(short token) { ValidateToken(token); if (!_completed) { ManualResetValueTaskSourceCoreShared.ThrowInvalidOperationException(); } _error?.Throw(); return(_result); }
/// <summary>Schedules the continuation action for this operation.</summary> /// <param name="continuation">The continuation to invoke when the operation has completed.</param> /// <param name="state">The state object to pass to <paramref name="continuation"/> when it's invoked.</param> /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param> /// <param name="flags">The flags describing the behavior of the continuation.</param> public void OnCompleted(Action <object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { if (continuation == null) { throw new ArgumentNullException(nameof(continuation)); } ValidateToken(token); if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0) { _executionContext = ExecutionContext.Capture(); } if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != 0) { SynchronizationContext sc = SynchronizationContext.Current; if (sc != null && sc.GetType() != typeof(SynchronizationContext)) { _capturedContext = sc; } else { TaskScheduler ts = TaskScheduler.Current; if (ts != TaskScheduler.Default) { _capturedContext = ts; } } } // We need to set the continuation state before we swap in the delegate, so that // if there's a race between this and SetResult/Exception and SetResult/Exception // sees the _continuation as non-null, it'll be able to invoke it with the state // stored here. However, this also means that if this is used incorrectly (e.g. // awaited twice concurrently), _continuationState might get erroneously overwritten. // To minimize the chances of that, we check preemptively whether _continuation // is already set to something other than the completion sentinel. object oldContinuation = _continuation; if (oldContinuation == null) { _continuationState = state; oldContinuation = Interlocked.CompareExchange(ref _continuation, continuation, null); } if (oldContinuation != null) { // Operation already completed, so we need to queue the supplied callback. if (!ReferenceEquals(oldContinuation, ManualResetValueTaskSourceCoreShared.s_sentinel)) { ManualResetValueTaskSourceCoreShared.ThrowInvalidOperationException(); } switch (_capturedContext) { case null: if (_executionContext != null) { ThreadPool.QueueUserWorkItem(continuation, state, preferLocal: true); } else { ThreadPool.UnsafeQueueUserWorkItem(continuation, state, preferLocal: true); } break; case SynchronizationContext sc: sc.Post(s => { var tuple = (Tuple <Action <object>, object>)s; tuple.Item1(tuple.Item2); }, Tuple.Create(continuation, state)); break; case TaskScheduler ts: Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts); break; } } }
public void OnCompleted(Action <object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) { if (continuation == null) { throw new ArgumentNullException(nameof(continuation)); } ValidateToken(token); if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0) { _executionContext = ExecutionContext.Capture(); } if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != 0) { SynchronizationContext sc = SynchronizationContext.Current; if (sc != null && sc.GetType() != typeof(SynchronizationContext)) { _capturedContext = sc; } else { TaskScheduler ts = TaskScheduler.Current; if (ts != TaskScheduler.Default) { _capturedContext = ts; } } } object oldContinuation = _continuation; if (oldContinuation == null) { _continuationState = state; oldContinuation = Interlocked.CompareExchange(ref _continuation, continuation, null); } if (oldContinuation != null) { if (!ReferenceEquals(oldContinuation, ManualResetValueTaskSourceCoreShared.s_sentinel)) { ManualResetValueTaskSourceCoreShared.ThrowInvalidOperationException(); } switch (_capturedContext) { case null: if (_executionContext != null) { // REVIEW: Original call was // ThreadPool.QueueUserWorkItem(continuation, state, preferLocal: true); ThreadPool.QueueUserWorkItem(s => continuation(s), state); } else { // REVIEW: Original call was // ThreadPool.UnsafeQueueUserWorkItem(continuation, state, preferLocal: true); ThreadPool.UnsafeQueueUserWorkItem(s => continuation(s), state); } break; case SynchronizationContext sc: sc.Post(s => { var tuple = (Tuple <Action <object>, object>)s; tuple.Item1(tuple.Item2); }, Tuple.Create(continuation, state)); break; case TaskScheduler ts: Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts); break; } } }