// 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();
}
}