// 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 { /// /// Provides a value type that wraps a and a , /// only one of which is used. /// /// The type of the result. /// /// /// 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 for each call will /// be prohibitive. /// /// /// There are tradeoffs to using a instead of a . /// For example, while a can help avoid an allocation in the case where the /// successful result is available synchronously, it also contains two fields whereas a /// 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. /// /// /// Further, for uses other than consuming the result of an asynchronous operation via await, /// 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 /// with a cached task as a common result or a . If the consumer of the result /// wants to use it as a , such as to use with in methods like Task.WhenAll and Task.WhenAny, /// the would first need to be converted into a using /// , which leads to an allocation that would have been avoided if a cached /// had been used in the first place. /// /// /// As such, the default choice for any asynchronous method should be to return a or /// . Only if performance analysis proves it worthwhile should a /// be used instead of . There is no non-generic version of /// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where /// a -returning method completes synchronously and successfully. /// /// [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))] [StructLayout(LayoutKind.Auto)] public struct ValueTask : IEquatable> { /// The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully. internal readonly Task _task; /// The result to be used if the operation completed successfully synchronously. internal readonly TResult _result; /// Initialize the with the result of the successful operation. /// The result. public ValueTask(TResult result) { _task = null; _result = result; } /// /// Initialize the with a that represents the operation. /// /// The task. public ValueTask(Task task) { if (task == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task); } _task = task; _result = default(TResult); } /// Returns the hash code for this instance. public override int GetHashCode() => _task != null ? _task.GetHashCode() : _result != null ? _result.GetHashCode() : 0; /// Returns a value indicating whether this value is equal to a specified . public override bool Equals(object obj) => obj is ValueTask && Equals((ValueTask)obj); /// Returns a value indicating whether this value is equal to a specified value. public bool Equals(ValueTask other) => _task != null || other._task != null ? _task == other._task : EqualityComparer.Default.Equals(_result, other._result); /// Returns a value indicating whether two values are equal. public static bool operator==(ValueTask left, ValueTask right) => left.Equals(right); /// Returns a value indicating whether two values are not equal. public static bool operator!=(ValueTask left, ValueTask right) => !left.Equals(right); /// /// Gets a 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. /// public Task 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.GetTaskForResult(_result); internal Task AsTaskExpectNonNull() => // Return the task if we were constructed from one, otherwise manufacture one. // Unlike AsTask(), this method is called only when we expect _task to be non-null, // and thus we don't want GetTaskForResult inlined. _task ?? GetTaskForResultNoInlining(); [MethodImpl(MethodImplOptions.NoInlining)] private Task GetTaskForResultNoInlining() => AsyncTaskMethodBuilder.GetTaskForResult(_result); /// Gets whether the represents a completed operation. public bool IsCompleted => _task == null || _task.IsCompleted; /// Gets whether the represents a successfully completed operation. public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully; /// Gets whether the represents a failed operation. public bool IsFaulted => _task != null && _task.IsFaulted; /// Gets whether the represents a canceled operation. public bool IsCanceled => _task != null && _task.IsCanceled; /// Gets the result. public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult(); /// Gets an awaiter for this value. public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this); /// Configures an awaiter for this value. /// /// true to attempt to marshal the continuation back to the captured context; otherwise, false. /// public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) => new ConfiguredValueTaskAwaitable(this, continueOnCapturedContext); /// Gets a string-representation of this . 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. /// Creates a method builder for use with an async method. /// The created builder. [EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption public static AsyncValueTaskMethodBuilder CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder.Create(); } }