1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
9 using System.Threading.Tasks.Sources;
12 using Internal.Runtime.CompilerServices;
15 namespace System.Threading.Tasks
17 // TYPE SAFETY WARNING:
18 // This code uses Unsafe.As to cast _obj. This is done in order to minimize the costs associated with
19 // casting _obj to a variety of different types that can be stored in a ValueTask, e.g. Task<TResult>
20 // vs IValueTaskSource<TResult>. Previous attempts at this were faulty due to using a separate field
21 // to store information about the type of the object in _obj; this is faulty because if the ValueTask
22 // is stored into a field, concurrent read/writes can result in tearing the _obj from the type information
23 // stored in a separate field. This means we can rely only on the _obj field to determine how to handle
24 // it. As such, the pattern employed is to copy _obj into a local obj, and then check it for null and
25 // type test against Task/Task<TResult>. Since the ValueTask can only be constructed with null, Task,
26 // or IValueTaskSource, we can then be confident in knowing that if it doesn't match one of those values,
27 // it must be an IValueTaskSource, and we can use Unsafe.As. This could be defeated by other unsafe means,
28 // like private reflection or using Unsafe.As manually, but at that point you're already doing things
29 // that can violate type safety; we only care about getting correct behaviors when using "safe" code.
30 // There are still other race conditions in user's code that can result in errors, but such errors don't
31 // cause ValueTask to violate type safety.
33 /// <summary>Provides an awaitable result of an asynchronous operation.</summary>
35 /// <see cref="ValueTask"/>s are meant to be directly awaited. To do more complicated operations with them, a <see cref="Task"/>
36 /// should be extracted using <see cref="AsTask"/>. Such operations might include caching an instance to be awaited later,
37 /// registering multiple continuations with a single operation, awaiting the same task multiple times, and using combinators over
38 /// multiple operations.
40 [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder))]
41 [StructLayout(LayoutKind.Auto)]
42 public readonly struct ValueTask : IEquatable<ValueTask>
44 /// <summary>A task canceled using `new CancellationToken(true)`.</summary>
45 private static readonly Task s_canceledTask =
47 Task.Delay(Timeout.Infinite, new CancellationToken(canceled: true));
49 Task.FromCanceled(new CancellationToken(canceled: true));
51 /// <summary>A successfully completed task.</summary>
52 internal static Task CompletedTask
54 { get; } = Task.Delay(0);
56 => Task.CompletedTask;
59 /// <summary>null if representing a successful synchronous completion, otherwise a <see cref="Task"/> or a <see cref="IValueTaskSource"/>.</summary>
60 internal readonly object _obj;
61 /// <summary>Opaque value passed through to the <see cref="IValueTaskSource"/>.</summary>
62 internal readonly short _token;
63 /// <summary>true to continue on the capture context; otherwise, true.</summary>
64 /// <remarks>Stored in the <see cref="ValueTask"/> rather than in the configured awaiter to utilize otherwise padding space.</remarks>
65 internal readonly bool _continueOnCapturedContext;
67 // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation.
69 /// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="Task"/> that represents the operation.</summary>
70 /// <param name="task">The task.</param>
71 [MethodImpl(MethodImplOptions.AggressiveInlining)]
72 public ValueTask(Task task)
76 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
81 _continueOnCapturedContext = true;
85 /// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="IValueTaskSource"/> object that represents the operation.</summary>
86 /// <param name="source">The source.</param>
87 /// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
88 [MethodImpl(MethodImplOptions.AggressiveInlining)]
89 public ValueTask(IValueTaskSource source, short token)
93 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
99 _continueOnCapturedContext = true;
102 [MethodImpl(MethodImplOptions.AggressiveInlining)]
103 private ValueTask(object obj, short token, bool continueOnCapturedContext)
107 _continueOnCapturedContext = continueOnCapturedContext;
110 /// <summary>Returns the hash code for this instance.</summary>
111 public override int GetHashCode() => _obj?.GetHashCode() ?? 0;
113 /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
114 public override bool Equals(object obj) =>
116 Equals((ValueTask)obj);
118 /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask"/> value.</summary>
119 public bool Equals(ValueTask other) => _obj == other._obj && _token == other._token;
121 /// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are equal.</summary>
122 public static bool operator ==(ValueTask left, ValueTask right) =>
125 /// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are not equal.</summary>
126 public static bool operator !=(ValueTask left, ValueTask right) =>
130 /// Gets a <see cref="Task"/> object to represent this ValueTask.
133 /// It will either return the wrapped task object if one exists, or it'll
134 /// manufacture a new task object to represent the result.
139 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
141 obj == null ? CompletedTask :
143 GetTaskForValueTaskSource(Unsafe.As<IValueTaskSource>(obj));
146 /// <summary>Gets a <see cref="ValueTask"/> that may be used at any point in the future.</summary>
147 public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
149 /// <summary>Creates a <see cref="Task"/> to represent the <see cref="IValueTaskSource"/>.</summary>
151 /// The <see cref="IValueTaskSource"/> is passed in rather than reading and casting <see cref="_obj"/>
152 /// so that the caller can pass in an object it's already validated.
154 private Task GetTaskForValueTaskSource(IValueTaskSource t)
156 ValueTaskSourceStatus status = t.GetStatus(_token);
157 if (status != ValueTaskSourceStatus.Pending)
161 // Propagate any exceptions that may have occurred, then return
162 // an already successfully completed task.
164 return CompletedTask;
166 // If status is Faulted or Canceled, GetResult should throw. But
167 // we can't guarantee every implementation will do the "right thing".
168 // If it doesn't throw, we just treat that as success and ignore
171 catch (Exception exc)
173 if (status == ValueTaskSourceStatus.Canceled)
176 if (exc is OperationCanceledException oce)
178 var task = new Task<VoidTaskResult>();
179 task.TrySetCanceled(oce.CancellationToken, oce);
183 return s_canceledTask;
188 var tcs = new TaskCompletionSource<bool>();
189 tcs.TrySetException(exc);
192 return Task.FromException(exc);
198 var m = new ValueTaskSourceAsTask(t, _token);
207 /// <summary>Type used to create a <see cref="Task"/> to represent a <see cref="IValueTaskSource"/>.</summary>
208 private sealed class ValueTaskSourceAsTask :
210 TaskCompletionSource<bool>
215 private static readonly Action<object> s_completionAction = state =>
217 if (!(state is ValueTaskSourceAsTask vtst) ||
218 !(vtst._source is IValueTaskSource source))
220 // This could only happen if the IValueTaskSource passed the wrong state
221 // or if this callback were invoked multiple times such that the state
222 // was previously nulled out.
223 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
228 ValueTaskSourceStatus status = source.GetStatus(vtst._token);
231 source.GetResult(vtst._token);
232 vtst.TrySetResult(default);
234 catch (Exception exc)
236 if (status == ValueTaskSourceStatus.Canceled)
239 vtst.TrySetCanceled();
241 if (exc is OperationCanceledException oce)
243 vtst.TrySetCanceled(oce.CancellationToken, oce);
247 vtst.TrySetCanceled(new CancellationToken(true));
253 vtst.TrySetException(exc);
258 /// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
259 private IValueTaskSource _source;
260 /// <summary>The token to pass through to operations on <see cref="_source"/></summary>
261 private readonly short _token;
263 public ValueTaskSourceAsTask(IValueTaskSource source, short token)
267 source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
271 /// <summary>Gets whether the <see cref="ValueTask"/> represents a completed operation.</summary>
272 public bool IsCompleted
274 [MethodImpl(MethodImplOptions.AggressiveInlining)]
278 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
287 return t.IsCompleted;
290 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
294 /// <summary>Gets whether the <see cref="ValueTask"/> represents a successfully completed operation.</summary>
295 public bool IsCompletedSuccessfully
297 [MethodImpl(MethodImplOptions.AggressiveInlining)]
301 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
312 t.Status == TaskStatus.RanToCompletion;
314 t.IsCompletedSuccessfully;
318 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
322 /// <summary>Gets whether the <see cref="ValueTask"/> represents a failed operation.</summary>
323 public bool IsFaulted
328 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
340 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
344 /// <summary>Gets whether the <see cref="ValueTask"/> represents a canceled operation.</summary>
346 /// If the <see cref="ValueTask"/> is backed by a result or by a <see cref="IValueTaskSource"/>,
347 /// this will always return false. If it's backed by a <see cref="Task"/>, it'll return the
348 /// value of the task's <see cref="Task.IsCanceled"/> property.
350 public bool IsCanceled
355 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
367 return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
371 /// <summary>Throws the exception that caused the <see cref="ValueTask"/> to fail. If it completed successfully, nothing is thrown.</summary>
372 [MethodImpl(MethodImplOptions.AggressiveInlining)]
374 internal void ThrowIfCompletedUnsuccessfully()
377 Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
384 t.GetAwaiter().GetResult();
386 TaskAwaiter.ValidateEnd(t);
391 Unsafe.As<IValueTaskSource>(obj).GetResult(_token);
396 /// <summary>Gets an awaiter for this <see cref="ValueTask"/>.</summary>
397 public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this);
399 /// <summary>Configures an awaiter for this <see cref="ValueTask"/>.</summary>
400 /// <param name="continueOnCapturedContext">
401 /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
403 [MethodImpl(MethodImplOptions.AggressiveInlining)]
404 public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) =>
405 new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, continueOnCapturedContext));
408 /// <summary>Provides a value type that can represent a synchronously available value or a task object.</summary>
409 /// <typeparam name="TResult">Specifies the type of the result.</typeparam>
411 /// <see cref="ValueTask{TResult}"/>s are meant to be directly awaited. To do more complicated operations with them, a <see cref="Task"/>
412 /// should be extracted using <see cref="AsTask"/> or <see cref="Preserve"/>. Such operations might include caching an instance to
413 /// be awaited later, registering multiple continuations with a single operation, awaiting the same task multiple times, and using
414 /// combinators over multiple operations.
416 [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
417 [StructLayout(LayoutKind.Auto)]
418 public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
420 /// <summary>A task canceled using `new CancellationToken(true)`. Lazily created only when first needed.</summary>
421 private static Task<TResult> s_canceledTask;
422 /// <summary>null if <see cref="_result"/> has the result, otherwise a <see cref="Task{TResult}"/> or a <see cref="IValueTaskSource{TResult}"/>.</summary>
423 internal readonly object _obj;
424 /// <summary>The result to be used if the operation completed successfully synchronously.</summary>
425 internal readonly TResult _result;
426 /// <summary>Opaque value passed through to the <see cref="IValueTaskSource{TResult}"/>.</summary>
427 internal readonly short _token;
428 /// <summary>true to continue on the captured context; otherwise, false.</summary>
429 /// <remarks>Stored in the <see cref="ValueTask{TResult}"/> rather than in the configured awaiter to utilize otherwise padding space.</remarks>
430 internal readonly bool _continueOnCapturedContext;
432 // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation
433 // with a result of default(TResult).
435 /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <typeparamref name="TResult"/> result value.</summary>
436 /// <param name="result">The result.</param>
437 [MethodImpl(MethodImplOptions.AggressiveInlining)]
438 public ValueTask(TResult result)
443 _continueOnCapturedContext = true;
447 /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.</summary>
448 /// <param name="task">The task.</param>
449 [MethodImpl(MethodImplOptions.AggressiveInlining)]
450 public ValueTask(Task<TResult> task)
454 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
460 _continueOnCapturedContext = true;
464 /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="IValueTaskSource{TResult}"/> object that represents the operation.</summary>
465 /// <param name="source">The source.</param>
466 /// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
467 [MethodImpl(MethodImplOptions.AggressiveInlining)]
468 public ValueTask(IValueTaskSource<TResult> source, short token)
472 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
479 _continueOnCapturedContext = true;
482 /// <summary>Non-verified initialization of the struct to the specified values.</summary>
483 /// <param name="obj">The object.</param>
484 /// <param name="result">The result.</param>
485 /// <param name="token">The token.</param>
486 /// <param name="continueOnCapturedContext">true to continue on captured context; otherwise, false.</param>
487 [MethodImpl(MethodImplOptions.AggressiveInlining)]
488 private ValueTask(object obj, TResult result, short token, bool continueOnCapturedContext)
493 _continueOnCapturedContext = continueOnCapturedContext;
497 /// <summary>Returns the hash code for this instance.</summary>
498 public override int GetHashCode() =>
499 _obj != null ? _obj.GetHashCode() :
500 _result != null ? _result.GetHashCode() :
503 /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
504 public override bool Equals(object obj) =>
505 obj is ValueTask<TResult> &&
506 Equals((ValueTask<TResult>)obj);
508 /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask{TResult}"/> value.</summary>
509 public bool Equals(ValueTask<TResult> other) =>
510 _obj != null || other._obj != null ?
511 _obj == other._obj && _token == other._token :
512 EqualityComparer<TResult>.Default.Equals(_result, other._result);
514 /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are equal.</summary>
515 public static bool operator ==(ValueTask<TResult> left, ValueTask<TResult> right) =>
518 /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are not equal.</summary>
519 public static bool operator !=(ValueTask<TResult> left, ValueTask<TResult> right) =>
523 /// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask.
526 /// It will either return the wrapped task object if one exists, or it'll
527 /// manufacture a new task object to represent the result.
529 public Task<TResult> AsTask()
532 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
538 Task.FromResult(_result);
540 AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
544 if (obj is Task<TResult> t)
549 return GetTaskForValueTaskSource(Unsafe.As<IValueTaskSource<TResult>>(obj));
552 /// <summary>Gets a <see cref="ValueTask{TResult}"/> that may be used at any point in the future.</summary>
553 public ValueTask<TResult> Preserve() => _obj == null ? this : new ValueTask<TResult>(AsTask());
555 /// <summary>Creates a <see cref="Task{TResult}"/> to represent the <see cref="IValueTaskSource{TResult}"/>.</summary>
557 /// The <see cref="IValueTaskSource{TResult}"/> is passed in rather than reading and casting <see cref="_obj"/>
558 /// so that the caller can pass in an object it's already validated.
560 private Task<TResult> GetTaskForValueTaskSource(IValueTaskSource<TResult> t)
562 ValueTaskSourceStatus status = t.GetStatus(_token);
563 if (status != ValueTaskSourceStatus.Pending)
567 // Get the result of the operation and return a task for it.
568 // If any exception occurred, propagate it
571 Task.FromResult(t.GetResult(_token));
573 AsyncTaskMethodBuilder<TResult>.GetTaskForResult(t.GetResult(_token));
576 // If status is Faulted or Canceled, GetResult should throw. But
577 // we can't guarantee every implementation will do the "right thing".
578 // If it doesn't throw, we just treat that as success and ignore
581 catch (Exception exc)
583 if (status == ValueTaskSourceStatus.Canceled)
586 if (exc is OperationCanceledException oce)
588 var task = new Task<TResult>();
589 task.TrySetCanceled(oce.CancellationToken, oce);
594 Task<TResult> canceledTask = s_canceledTask;
595 if (canceledTask == null)
598 var tcs = new TaskCompletionSource<TResult>();
599 tcs.TrySetCanceled();
600 canceledTask = tcs.Task;
602 canceledTask = Task.FromCanceled<TResult>(new CancellationToken(true));
604 // Benign race condition to initialize cached task, as identity doesn't matter.
605 s_canceledTask = canceledTask;
612 var tcs = new TaskCompletionSource<TResult>();
613 tcs.TrySetException(exc);
616 return Task.FromException<TResult>(exc);
622 var m = new ValueTaskSourceAsTask(t, _token);
631 /// <summary>Type used to create a <see cref="Task{TResult}"/> to represent a <see cref="IValueTaskSource{TResult}"/>.</summary>
632 private sealed class ValueTaskSourceAsTask :
634 TaskCompletionSource<TResult>
639 private static readonly Action<object> s_completionAction = state =>
641 if (!(state is ValueTaskSourceAsTask vtst) ||
642 !(vtst._source is IValueTaskSource<TResult> source))
644 // This could only happen if the IValueTaskSource<TResult> passed the wrong state
645 // or if this callback were invoked multiple times such that the state
646 // was previously nulled out.
647 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
652 ValueTaskSourceStatus status = source.GetStatus(vtst._token);
655 vtst.TrySetResult(source.GetResult(vtst._token));
657 catch (Exception exc)
659 if (status == ValueTaskSourceStatus.Canceled)
662 vtst.TrySetCanceled();
664 if (exc is OperationCanceledException oce)
666 vtst.TrySetCanceled(oce.CancellationToken, oce);
670 vtst.TrySetCanceled(new CancellationToken(true));
676 vtst.TrySetException(exc);
681 /// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
682 private IValueTaskSource<TResult> _source;
683 /// <summary>The token to pass through to operations on <see cref="_source"/></summary>
684 private readonly short _token;
686 public ValueTaskSourceAsTask(IValueTaskSource<TResult> source, short token)
690 source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
694 /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary>
695 public bool IsCompleted
697 [MethodImpl(MethodImplOptions.AggressiveInlining)]
701 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
708 if (obj is Task<TResult> t)
710 return t.IsCompleted;
713 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
717 /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary>
718 public bool IsCompletedSuccessfully
720 [MethodImpl(MethodImplOptions.AggressiveInlining)]
724 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
731 if (obj is Task<TResult> t)
735 t.Status == TaskStatus.RanToCompletion;
737 t.IsCompletedSuccessfully;
741 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
745 /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary>
746 public bool IsFaulted
751 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
758 if (obj is Task<TResult> t)
763 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
767 /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary>
769 /// If the <see cref="ValueTask{TResult}"/> is backed by a result or by a <see cref="IValueTaskSource{TResult}"/>,
770 /// this will always return false. If it's backed by a <see cref="Task"/>, it'll return the
771 /// value of the task's <see cref="Task.IsCanceled"/> property.
773 public bool IsCanceled
778 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
785 if (obj is Task<TResult> t)
790 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
794 /// <summary>Gets the result.</summary>
795 public TResult Result
797 [MethodImpl(MethodImplOptions.AggressiveInlining)]
801 Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
808 if (obj is Task<TResult> t)
811 return t.GetAwaiter().GetResult();
813 TaskAwaiter.ValidateEnd(t);
814 return t.ResultOnSuccess;
818 return Unsafe.As<IValueTaskSource<TResult>>(obj).GetResult(_token);
822 /// <summary>Gets an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
823 [MethodImpl(MethodImplOptions.AggressiveInlining)]
824 public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this);
826 /// <summary>Configures an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
827 /// <param name="continueOnCapturedContext">
828 /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
830 [MethodImpl(MethodImplOptions.AggressiveInlining)]
831 public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) =>
832 new ConfiguredValueTaskAwaitable<TResult>(new ValueTask<TResult>(_obj, _result, _token, continueOnCapturedContext));
834 /// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
835 public override string ToString()
837 if (IsCompletedSuccessfully)
839 TResult result = Result;
842 return result.ToString();