This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new System.Threading.Tasks.Extensions library
For now, it contains just ```ValueTask<TResult>``` and its awaiter.
- Loading branch information
1 parent
b846054
commit 030bd85
Showing
10 changed files
with
2,927 additions
and
0 deletions.
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
src/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.sln
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
25 changes: 25 additions & 0 deletions
25
src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
48 changes: 48 additions & 0 deletions
48
...System.Threading.Tasks.Extensions/src/System/Runtime/CompilerServices/ValueTaskAwaiter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
166 changes: 166 additions & 0 deletions
166
src/System.Threading.Tasks.Extensions/src/System/Threading/Tasks/ValueTask.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": {} | ||
} | ||
} |
Oops, something went wrong.