summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs')
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs
new file mode 100644
index 0000000000..384e4a8ab3
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs
@@ -0,0 +1,169 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Provides a value type that wraps a <see cref="Task{TResult}"/> and a <typeparamref name="TResult"/>,
+ /// only one of which is used.
+ /// </summary>
+ /// <typeparam name="TResult">The type of the result.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// Methods may return an instance of this value type when it's likely that the result of their
+ /// operations will be available synchronously and when the method is expected to be invoked so
+ /// frequently that the cost of allocating a new <see cref="Task{TResult}"/> for each call will
+ /// be prohibitive.
+ /// </para>
+ /// <para>
+ /// There are tradeoffs to using a <see cref="ValueTask{TResult}"/> instead of a <see cref="Task{TResult}"/>.
+ /// For example, while a <see cref="ValueTask{TResult}"/> can help avoid an allocation in the case where the
+ /// successful result is available synchronously, it also contains two fields whereas a <see cref="Task{TResult}"/>
+ /// as a reference type is a single field. This means that a method call ends up returning two fields worth of
+ /// data instead of one, which is more data to copy. It also means that if a method that returns one of these
+ /// is awaited within an async method, the state machine for that async method will be larger due to needing
+ /// to store the struct that's two fields instead of a single reference.
+ /// </para>
+ /// <para>
+ /// Further, for uses other than consuming the result of an asynchronous operation via await,
+ /// <see cref="ValueTask{TResult}"/> can lead to a more convoluted programming model, which can in turn actually
+ /// lead to more allocations. For example, consider a method that could return either a <see cref="Task{TResult}"/>
+ /// with a cached task as a common result or a <see cref="ValueTask{TResult}"/>. If the consumer of the result
+ /// wants to use it as a <see cref="Task{TResult}"/>, such as to use with in methods like Task.WhenAll and Task.WhenAny,
+ /// the <see cref="ValueTask{TResult}"/> would first need to be converted into a <see cref="Task{TResult}"/> using
+ /// <see cref="ValueTask{TResult}.AsTask"/>, which leads to an allocation that would have been avoided if a cached
+ /// <see cref="Task{TResult}"/> had been used in the first place.
+ /// </para>
+ /// <para>
+ /// As such, the default choice for any asynchronous method should be to return a <see cref="Task"/> or
+ /// <see cref="Task{TResult}"/>. Only if performance analysis proves it worthwhile should a <see cref="ValueTask{TResult}"/>
+ /// be used instead of <see cref="Task{TResult}"/>. There is no non-generic version of <see cref="ValueTask{TResult}"/>
+ /// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where
+ /// a <see cref="Task"/>-returning method completes synchronously and successfully.
+ /// </para>
+ /// </remarks>
+ [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
+ {
+ /// <summary>The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully.</summary>
+ internal readonly Task<TResult> _task;
+ /// <summary>The result to be used if the operation completed successfully synchronously.</summary>
+ internal readonly TResult _result;
+
+ /// <summary>Initialize the <see cref="ValueTask{TResult}"/> with the result of the successful operation.</summary>
+ /// <param name="result">The result.</param>
+ public ValueTask(TResult result)
+ {
+ _task = null;
+ _result = result;
+ }
+
+ /// <summary>
+ /// Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.
+ /// </summary>
+ /// <param name="task">The task.</param>
+ public ValueTask(Task<TResult> task)
+ {
+ if (task == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
+ }
+
+ _task = task;
+ _result = default(TResult);
+ }
+
+ /// <summary>Returns the hash code for this instance.</summary>
+ public override int GetHashCode() =>
+ _task != null ? _task.GetHashCode() :
+ _result != null ? _result.GetHashCode() :
+ 0;
+
+ /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
+ public override bool Equals(object obj) =>
+ obj is ValueTask<TResult> &&
+ Equals((ValueTask<TResult>)obj);
+
+ /// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask{TResult}"/> value.</summary>
+ public bool Equals(ValueTask<TResult> other) =>
+ _task != null || other._task != null ?
+ _task == other._task :
+ EqualityComparer<TResult>.Default.Equals(_result, other._result);
+
+ /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are equal.</summary>
+ public static bool operator==(ValueTask<TResult> left, ValueTask<TResult> right) =>
+ left.Equals(right);
+
+ /// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are not equal.</summary>
+ public static bool operator!=(ValueTask<TResult> left, ValueTask<TResult> right) =>
+ !left.Equals(right);
+
+ /// <summary>
+ /// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask. It will
+ /// either return the wrapped task object if one exists, or it'll manufacture a new
+ /// task object to represent the result.
+ /// </summary>
+ public Task<TResult> AsTask() =>
+ // Return the task if we were constructed from one, otherwise manufacture one. We don't
+ // cache the generated task into _task as it would end up changing both equality comparison
+ // and the hash code we generate in GetHashCode.
+ _task ?? AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary>
+ public bool IsCompleted => _task == null || _task.IsCompleted;
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary>
+ public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully;
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary>
+ public bool IsFaulted => _task != null && _task.IsFaulted;
+
+ /// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary>
+ public bool IsCanceled => _task != null && _task.IsCanceled;
+
+ /// <summary>Gets the result.</summary>
+ public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult();
+
+ /// <summary>Gets an awaiter for this value.</summary>
+ public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this);
+
+ /// <summary>Configures an awaiter for this value.</summary>
+ /// <param name="continueOnCapturedContext">
+ /// true to attempt to marshal the continuation back to the captured context; otherwise, false.
+ /// </param>
+ public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) =>
+ new ConfiguredValueTaskAwaitable<TResult>(this, continueOnCapturedContext);
+
+ /// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
+ public override string ToString()
+ {
+ if (_task != null)
+ {
+ return _task.IsCompletedSuccessfully && _task.Result != null ?
+ _task.Result.ToString() :
+ string.Empty;
+ }
+ else
+ {
+ return _result != null ?
+ _result.ToString() :
+ string.Empty;
+ }
+ }
+
+ // TODO https://github.com/dotnet/corefx/issues/22171:
+ // Remove CreateAsyncMethodBuilder once the C# compiler relies on the AsyncBuilder attribute.
+
+ /// <summary>Creates a method builder for use with an async method.</summary>
+ /// <returns>The created builder.</returns>
+ [EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption
+ public static AsyncValueTaskMethodBuilder<TResult> CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder<TResult>.Create();
+ }
+}