Skip to content

Commit

Permalink
Implement NoThrowAwaitable for ValueTask<TResult>
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Jun 5, 2023
1 parent 67c9bd4 commit e8896e6
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 0 deletions.
132 changes: 132 additions & 0 deletions src/Microsoft.VisualStudio.Threading/TplExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,38 @@ public static NoThrowValueTaskAwaitable NoThrowAwaitable(this ValueTask task, bo
return new NoThrowValueTaskAwaitable(task, captureContext);
}

/// <summary>
/// Returns an awaitable for the specified task that will never throw, even if the source task
/// faults or is canceled.
/// </summary>
/// <remarks>
/// The awaitable returned by this method does not provide access to the result of a successfully-completed
/// <see cref="ValueTask{TResult}"/>. To await without throwing and use the resulting value, the following
/// pattern may be used:
///
/// <code>
/// var methodValueTask = MethodAsync().Preserve();
/// await methodValueTask.NoThrowAwaitable(true);
/// if (methodValueTask.IsCompletedSuccessfully)
/// {
/// var result = methodValueTask.Result;
/// }
/// else
/// {
/// var exception = methodValueTask.AsTask().Exception.InnerException;
/// }
/// </code>
/// </remarks>
/// <param name="task">The task whose completion should signal the completion of the returned awaitable.</param>
/// <param name="captureContext">if set to <see langword="true"/> the continuation will be scheduled on the caller's context; <see langword="false"/> to always execute the continuation on the threadpool.</param>
/// <returns>An awaitable.</returns>
/// <typeparam name="TResult">The type of the result.</typeparam>
[SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "The receiver type is disjoint.")]
public static NoThrowValueTaskAwaitable<TResult> NoThrowAwaitable<TResult>(this ValueTask<TResult> task, bool captureContext = true)
{
return new NoThrowValueTaskAwaitable<TResult>(task, captureContext);
}

/// <summary>
/// Consumes a task and doesn't do anything with it. Useful for fire-and-forget calls to async methods within async methods.
/// </summary>
Expand Down Expand Up @@ -876,6 +908,106 @@ public void GetResult()
}
}

/// <summary>
/// An awaitable that wraps a <see cref="ValueTask{TResult}"/> and never throws an exception when waited on.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
public readonly struct NoThrowValueTaskAwaitable<TResult>
{
/// <summary>
/// The task.
/// </summary>
private readonly ValueTask<TResult> task;

/// <summary>
/// A value indicating whether the continuation should be scheduled on the current sync context.
/// </summary>
private readonly bool captureContext;

/// <summary>
/// Initializes a new instance of the <see cref="NoThrowValueTaskAwaitable{TResult}" /> struct.
/// </summary>
/// <param name="task">The task.</param>
/// <param name="captureContext">Whether the continuation should be scheduled on the current sync context.</param>
public NoThrowValueTaskAwaitable(ValueTask<TResult> task, bool captureContext)
{
this.task = task.Preserve();
this.captureContext = captureContext;
}

/// <summary>
/// Gets the awaiter.
/// </summary>
/// <returns>The awaiter.</returns>
public NoThrowValueTaskAwaiter<TResult> GetAwaiter()
{
return new NoThrowValueTaskAwaiter<TResult>(this.task, this.captureContext);
}
}

/// <summary>
/// An awaiter that wraps a task and never throws an exception when waited on.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
public readonly struct NoThrowValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
{
/// <summary>
/// The task.
/// </summary>
private readonly ValueTask<TResult> task;

/// <summary>
/// A value indicating whether the continuation should be scheduled on the current sync context.
/// </summary>
private readonly bool captureContext;

/// <summary>
/// Initializes a new instance of the <see cref="NoThrowValueTaskAwaiter{TResult}"/> struct.
/// </summary>
/// <param name="task">The task.</param>
/// <param name="captureContext">if set to <see langword="true"/> [capture context].</param>
public NoThrowValueTaskAwaiter(ValueTask<TResult> task, bool captureContext)
{
this.task = task;
this.captureContext = captureContext;
}

/// <summary>
/// Gets a value indicating whether the task has completed.
/// </summary>
public bool IsCompleted
{
get { return this.task.IsCompleted; }
}

/// <summary>
/// Schedules a delegate for execution at the conclusion of a task's execution.
/// </summary>
/// <param name="continuation">The action.</param>
public void OnCompleted(Action continuation)
{
this.task.ConfigureAwait(this.captureContext).GetAwaiter().OnCompleted(continuation);
}

/// <summary>
/// Schedules a delegate for execution at the conclusion of a task's execution
/// without capturing the ExecutionContext.
/// </summary>
/// <param name="continuation">The action.</param>
public void UnsafeOnCompleted(Action continuation)
{
this.task.ConfigureAwait(this.captureContext).GetAwaiter().UnsafeOnCompleted(continuation);
}

/// <summary>
/// Does nothing.
/// </summary>
public void GetResult()
{
// No need to do anything with 'task' because we already called Preserve on it.
}
}

/// <summary>
/// A state bag for the <see cref="FollowCancelableTaskToCompletion"/> method.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.UnsafeOnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.UnsafeOnCompleted(System.Action! continuation) -> void
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable(this System.Threading.Tasks.ValueTask task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable<TResult>(this System.Threading.Tasks.ValueTask<TResult> task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
virtual Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock<TMoniker, TResource>.GetTaskSchedulerToPrepareResourcesForConcurrentAccess(TResource! resource) -> System.Threading.Tasks.TaskScheduler!
Microsoft.VisualStudio.Threading.JoinableTaskContext.Capture() -> string?
Microsoft.VisualStudio.Threading.JoinableTaskFactory.RunAsync(System.Func<System.Threading.Tasks.Task!>! asyncMethod, string? parentToken, Microsoft.VisualStudio.Threading.JoinableTaskCreationOptions creationOptions) -> Microsoft.VisualStudio.Threading.JoinableTask!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.UnsafeOnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.UnsafeOnCompleted(System.Action! continuation) -> void
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable(this System.Threading.Tasks.ValueTask task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable<TResult>(this System.Threading.Tasks.ValueTask<TResult> task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
virtual Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock<TMoniker, TResource>.GetTaskSchedulerToPrepareResourcesForConcurrentAccess(TResource! resource) -> System.Threading.Tasks.TaskScheduler!
Microsoft.VisualStudio.Threading.JoinableTaskContext.Capture() -> string?
Microsoft.VisualStudio.Threading.JoinableTaskFactory.RunAsync(System.Func<System.Threading.Tasks.Task!>! asyncMethod, string? parentToken, Microsoft.VisualStudio.Threading.JoinableTaskCreationOptions creationOptions) -> Microsoft.VisualStudio.Threading.JoinableTask!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.UnsafeOnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.UnsafeOnCompleted(System.Action! continuation) -> void
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable(this System.Threading.Tasks.ValueTask task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable<TResult>(this System.Threading.Tasks.ValueTask<TResult> task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
virtual Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock<TMoniker, TResource>.GetTaskSchedulerToPrepareResourcesForConcurrentAccess(TResource! resource) -> System.Threading.Tasks.TaskScheduler!
Microsoft.VisualStudio.Threading.JoinableTaskContext.Capture() -> string?
Microsoft.VisualStudio.Threading.JoinableTaskFactory.RunAsync(System.Func<System.Threading.Tasks.Task!>! asyncMethod, string? parentToken, Microsoft.VisualStudio.Threading.JoinableTaskCreationOptions creationOptions) -> Microsoft.VisualStudio.Threading.JoinableTask!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.GetAwaiter() -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>.NoThrowValueTaskAwaitable(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter.UnsafeOnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.GetResult() -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.IsCompleted.get -> bool
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.NoThrowValueTaskAwaiter(System.Threading.Tasks.ValueTask<TResult> task, bool captureContext) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.OnCompleted(System.Action! continuation) -> void
Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaiter<TResult>.UnsafeOnCompleted(System.Action! continuation) -> void
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable(this System.Threading.Tasks.ValueTask task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable
static Microsoft.VisualStudio.Threading.TplExtensions.NoThrowAwaitable<TResult>(this System.Threading.Tasks.ValueTask<TResult> task, bool captureContext = true) -> Microsoft.VisualStudio.Threading.TplExtensions.NoThrowValueTaskAwaitable<TResult>
virtual Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock<TMoniker, TResource>.GetTaskSchedulerToPrepareResourcesForConcurrentAccess(TResource! resource) -> System.Threading.Tasks.TaskScheduler!
Microsoft.VisualStudio.Threading.JoinableTaskContext.Capture() -> string?
Microsoft.VisualStudio.Threading.JoinableTaskFactory.RunAsync(System.Func<System.Threading.Tasks.Task!>! asyncMethod, string? parentToken, Microsoft.VisualStudio.Threading.JoinableTaskCreationOptions creationOptions) -> Microsoft.VisualStudio.Threading.JoinableTask!
Expand Down
Loading

0 comments on commit e8896e6

Please sign in to comment.