public async Task TaskFirst(ValueTaskSourceStatus status, bool async) { using var source = LazyTaskCompletionSource.Create(); Assert.False(source.HasTask); Assert.False(source.HasSource); var task = source.Task; Assert.True(source.HasTask); Assert.True(source.HasSource); switch (status) { case ValueTaskSourceStatus.Canceled: Assert.True(source.TrySetCanceled()); Assert.False(source.TrySetCanceled()); try { if (async) { await task; } else { task.GetAwaiter().GetResult(); } } catch (OperationCanceledException) { } break; case ValueTaskSourceStatus.Faulted: Assert.True(source.TrySetException(new TestException())); Assert.False(source.TrySetException(new TestException())); try { if (async) { await task; } else { task.GetAwaiter().GetResult(); } } catch (TestException) { } break; case ValueTaskSourceStatus.Succeeded: Assert.True(source.TrySetResult()); Assert.False(source.TrySetResult()); if (async) { await task; } else { task.Wait(); } Assert.NotSame(task, TaskUtils.CompletedTask); break; } }
public void Success() { status = ValueTaskSourceStatus.Succeeded; var previousContinuation = Interlocked.CompareExchange(ref this.continuation, CallbackCompleted, null); if (previousContinuation != null) { // Async work completed, continue with... continuation ExecutionContext ec = executionContext; if (ec == null) { InvokeContinuation(previousContinuation, this.state, forceAsync: false); } else { // This case should be relatively rare, as the async Task/ValueTask method builders // use the awaiter's UnsafeOnCompleted, so this will only happen with code that // explicitly uses the awaiter's OnCompleted instead. executionContext = null; ExecutionContext.Run(ec, runState => { var t = (Tuple <_source, Action <object>, object>)runState; t.Item1.InvokeContinuation(t.Item2, t.Item3, forceAsync: false); }, Tuple.Create(this, previousContinuation, this.state)); } } }
/// <summary>Creates a <see cref="Task{TResult}"/> to represent the <see cref="IValueTaskSource{TResult}"/>.</summary> /// <remarks> /// The <see cref="IValueTaskSource{TResult}"/> is passed in rather than reading and casting <see cref="_obj"/> /// so that the caller can pass in an object it's already validated. /// </remarks> private Task <TResult> GetTaskForValueTaskSource(IValueTaskSource <TResult> t) { ValueTaskSourceStatus status = t.GetStatus(_token); if (status != ValueTaskSourceStatus.Pending) { try { // Get the result of the operation and return a task for it. // If any exception occurred, propagate it return(AsyncTaskMethodBuilder <TResult> .GetTaskForResult(t.GetResult(_token))); // If status is Faulted or Canceled, GetResult should throw. But // we can't guarantee every implementation will do the "right thing". // If it doesn't throw, we just treat that as success and ignore // the status. } catch (Exception exc) { if (status == ValueTaskSourceStatus.Canceled) { if (exc is OperationCanceledException oce) { var task = new Task <TResult>(); task.TrySetCanceled(oce.CancellationToken, oce); return(task); } Task <TResult> canceledTask = s_canceledTask; if (canceledTask == null) { // Benign race condition to initialize cached task, as identity doesn't matter. s_canceledTask = Task.FromCanceled <TResult>(new CancellationToken(true)); } return(canceledTask); } else { return(Task.FromException <TResult>(exc)); } } } return(new ValueTaskSourceAsTask(t, _token)); }
/// <summary>Creates a <see cref="Task"/> to represent the <see cref="IValueTaskSource"/>.</summary> /// <remarks> /// The <see cref="IValueTaskSource"/> is passed in rather than reading and casting <see cref="_obj"/> /// so that the caller can pass in an object it's already validated. /// </remarks> private Task GetTaskForValueTaskSource(IValueTaskSource t) { ValueTaskSourceStatus status = t.GetStatus(_token); if (status != ValueTaskSourceStatus.Pending) { try { // Propagate any exceptions that may have occurred, then return // an already successfully completed task. t.GetResult(_token); return(Task.CompletedTask); // If status is Faulted or Canceled, GetResult should throw. But // we can't guarantee every implementation will do the "right thing". // If it doesn't throw, we just treat that as success and ignore // the status. } catch (Exception exc) { if (status == ValueTaskSourceStatus.Canceled) { if (exc is OperationCanceledException oce) { var task = new Task(); task.TrySetCanceled(oce.CancellationToken, oce); return(task); } // Benign race condition to initialize cached task, as identity doesn't matter. return(s_canceledTask ??= Task.FromCanceled(new CancellationToken(canceled: true))); } else { return(Task.FromException(exc)); } } } return(new ValueTaskSourceAsTask(t, _token)); }
private Task GetTaskForValueTaskSource(IValueTaskSource t) { ValueTaskSourceStatus status = t.GetStatus(_token); if (status == ValueTaskSourceStatus.Pending) { return(new ValueTaskSourceAsTask(t, _token).Task); } try { t.GetResult(_token); return(CompletedTask); } catch (Exception ex) { if (status == ValueTaskSourceStatus.Canceled) { return(s_canceledTask); } TaskCompletionSource <Boolean> completionSource = new TaskCompletionSource <Boolean>(); completionSource.TrySetException(ex); return(completionSource.Task); } }
public void Cancel() { status = ValueTaskSourceStatus.Canceled; }