Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Add new System.Threading.Tasks.Extensions library
Browse files Browse the repository at this point in the history
For now, it contains just ```ValueTask<TResult>``` and its awaiter.
  • Loading branch information
stephentoub committed Dec 7, 2015
1 parent b846054 commit 030bd85
Show file tree
Hide file tree
Showing 10 changed files with 2,927 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23103.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Tasks.Extensions", "src\System.Threading.Tasks.Extensions.csproj", "{F24D3391-2928-4E83-AADE-B34423498750}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Tasks.Extensions.Tests", "tests\System.Threading.Tasks.Extensions.Tests.csproj", "{82B54697-0251-47A1-8546-FC507D0F3B08}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F24D3391-2928-4E83-AADE-B34423498750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F24D3391-2928-4E83-AADE-B34423498750}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F24D3391-2928-4E83-AADE-B34423498750}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F24D3391-2928-4E83-AADE-B34423498750}.Release|Any CPU.Build.0 = Release|Any CPU
{82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">AnyCPU</Platform>
<ProjectGuid>{F24D3391-2928-4E83-AADE-B34423498750}</ProjectGuid>
<OutputType>Library</OutputType>
<AssemblyName>System.Threading.Tasks.Extensions</AssemblyName>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>
<PackageTargetFramework>dotnet5.1</PackageTargetFramework>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
<ItemGroup>
<Compile Include="System\Runtime\CompilerServices\ValueTaskAwaiter.cs" />
<Compile Include="System\Threading\Tasks\ValueTask.cs" />
</ItemGroup>
<ItemGroup>
<None Include="project.json" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace System.Runtime.CompilerServices
{
/// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
[StructLayout(LayoutKind.Auto)]
public struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
{
/// <summary>The value being awaited.</summary>
private readonly ValueTask<TResult> _value;
/// <summary>The value to pass to ConfigureAwait.</summary>
private readonly bool _continueOnCapturedContext;

/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
/// <param name="continueOnCapturedContext">The value to pass to ConfigureAwait.</param>
public ValueTaskAwaiter(ValueTask<TResult> value, bool continueOnCapturedContext)
{
_value = value;
_continueOnCapturedContext = continueOnCapturedContext;
}

/// <summary>Returns this awaiter.</summary>
public ValueTaskAwaiter<TResult> GetAwaiter() { return this; }

/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> has completed.</summary>
public bool IsCompleted { get { return _value.IsCompleted; } }

/// <summary>Gets the result of the ValueTask.</summary>
public TResult GetResult() { return _value._task == null ? _value._result : _value._task.GetAwaiter().GetResult(); }

/// <summary>Schedules the continuation action for this ValueTask.</summary>
public void OnCompleted(Action continuation)
{
_value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}

/// <summary>Schedules the continuation action for this ValueTask.</summary>
public void UnsafeOnCompleted(Action continuation)
{
_value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Collections.Generic;
using System.Diagnostics;
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>
/// The default choice for any async 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>
[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)
{
Debug.Assert(task != null);
_task = task;
_result = default(TResult);
}

/// <summary>Implicit operator to wrap a <see cref="ValueTask{TResult}"/> around a task.</summary>
public static implicit operator ValueTask<TResult>(Task<TResult> task)
{
return new ValueTask<TResult>(task);
}

/// <summary>Implicit operator to wrap a <see cref="ValueTask{TResult}"/> around a result.</summary>
public static implicit operator ValueTask<TResult>(TResult result)
{
return new ValueTask<TResult>(result);
}

/// <summary>Returns the hash code for this instance.</summary>
public override int GetHashCode()
{
return
_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)
{
return
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)
{
return _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)
{
return 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)
{
return !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.
return _task ?? Task.FromResult(_result);
}

/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary>
public bool IsCompleted { get { return _task == null || _task.IsCompleted; } }

/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary>
public bool IsCompletedSuccessfully { get { return _task == null || _task.Status == TaskStatus.RanToCompletion; } }

/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary>
public bool IsFaulted { get { return _task != null && _task.IsFaulted; } }

/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary>
public bool IsCanceled { get { return _task != null && _task.IsCanceled; } }

/// <summary>Gets the result.</summary>
public TResult Result { get { return _task == null ? _result : _task.GetAwaiter().GetResult(); } }

/// <summary>Gets an awaiter for this value.</summary>
public ValueTaskAwaiter<TResult> GetAwaiter()
{
return new ValueTaskAwaiter<TResult>(this, continueOnCapturedContext: true);
}

/// <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 ValueTaskAwaiter<TResult> ConfigureAwait(bool continueOnCapturedContext)
{
return new ValueTaskAwaiter<TResult>(this, continueOnCapturedContext: continueOnCapturedContext);
}

/// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
public override string ToString()
{
return
_task == null ? _result.ToString() :
_task.Status == TaskStatus.RanToCompletion ? _task.Result.ToString() :
_task.Status.ToString();
}
}
}
11 changes: 11 additions & 0 deletions src/System.Threading.Tasks.Extensions/src/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"dependencies": {
"System.Collections": "4.0.0",
"System.Diagnostics.Debug": "4.0.0",
"System.Runtime": "4.0.0",
"System.Threading.Tasks": "4.0.0"
},
"frameworks": {
"dnxcore50": {}
}
}
Loading

0 comments on commit 030bd85

Please sign in to comment.