Imported Upstream version 5.0.0.42

Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2017-04-10 11:41:01 +00:00
parent 1190d13a04
commit 6bdd276d05
19939 changed files with 3099680 additions and 93811 deletions

View File

@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Tasks.Extensions.Tests", "tests\System.Threading.Tasks.Extensions.Tests.csproj", "{82B54697-0251-47A1-8546-FC507D0F3B08}"
ProjectSection(ProjectDependencies) = postProject
{} = {}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Tasks.Extensions", "src\System.Threading.Tasks.Extensions.csproj", "{F24D3391-2928-4E83-AADE-B34423498750}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
DebugNETCoreAppnetstandard1.0netstandard1.3|AnyCPU = DebugNETCoreAppnetstandard1.0netstandard1.3|AnyCPU
ReleaseNETCoreAppnetstandard1.0netstandard1.3|AnyCPU = ReleaseNETCoreAppnetstandard1.0netstandard1.3|AnyCPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{82B54697-0251-47A1-8546-FC507D0F3B08}.DebugNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.ActiveCfg = netstandard1.3-Debug|Any CPU
{82B54697-0251-47A1-8546-FC507D0F3B08}.DebugNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.Build.0 = netstandard1.3-Debug|Any CPU
{82B54697-0251-47A1-8546-FC507D0F3B08}.ReleaseNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.ActiveCfg = netstandard1.3-Release|Any CPU
{82B54697-0251-47A1-8546-FC507D0F3B08}.ReleaseNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.Build.0 = netstandard1.3-Release|Any CPU
{F24D3391-2928-4E83-AADE-B34423498750}.DebugNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.ActiveCfg = netstandard1.0-Debug|Any CPU
{F24D3391-2928-4E83-AADE-B34423498750}.DebugNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.Build.0 = netstandard1.0-Debug|Any CPU
{F24D3391-2928-4E83-AADE-B34423498750}.ReleaseNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.ActiveCfg = netstandard1.0-Release|Any CPU
{F24D3391-2928-4E83-AADE-B34423498750}.ReleaseNETCoreAppnetstandard1.0netstandard1.3|AnyCPU.Build.0 = netstandard1.0-Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\dir.props" />
<PropertyGroup>
<AssemblyVersion>4.1.1.0</AssemblyVersion>
<IsNETCoreApp>true</IsNETCoreApp>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<ItemGroup>
<Project Include="System.Threading.Tasks.Extensions.pkgproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" />
</Project>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<!-- we need to be supported on pre-nuget-3 platforms (Dev12, Dev11, etc) -->
<MinClientVersion>2.8.6</MinClientVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\System.Threading.Tasks.Extensions.builds">
<SupportedFramework>net45;netcore45;netcoreapp1.0;wpa81;wp8;$(AllXamarinFrameworks)</SupportedFramework>
</ProjectReference>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
netstandard1.0;
netcoreapp;
</BuildConfigurations>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<ProjectGuid>{F24D3391-2928-4E83-AADE-B34423498750}</ProjectGuid>
<AssemblyName>System.Threading.Tasks.Extensions</AssemblyName>
<DocumentationFile>$(OutputPath)$(AssemblyName).xml</DocumentationFile>
<UseOpenKey Condition="'$(UseOpenKey)'==''">true</UseOpenKey>
<PackageTargetFramework Condition="'$(TargetGroup)' == 'netstandard1.0'">netstandard1.0;portable-net45+win8+wp8+wpa81</PackageTargetFramework>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netstandard1.0-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netstandard1.0-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="System\Runtime\CompilerServices\AsyncMethodBuilderAttribute.cs" />
<Compile Include="System\Runtime\CompilerServices\AsyncValueTaskMethodBuilder.cs" />
<Compile Include="System\Runtime\CompilerServices\ConfiguredValueTaskAwaitable.cs" />
<Compile Include="System\Runtime\CompilerServices\ValueTaskAwaiter.cs" />
<Compile Include="System\Threading\Tasks\ValueTask.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Runtime" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>

View File

@@ -0,0 +1,24 @@
// 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.
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Indicates the type of the async method builder that should be used by a language compiler to
/// build the attributed type when used as the return type of an async method.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)]
public sealed class AsyncMethodBuilderAttribute : Attribute
{
/// <summary>Initializes the <see cref="AsyncMethodBuilderAttribute"/>.</summary>
/// <param name="builderType">The <see cref="Type"/> of the associated builder.</param>
public AsyncMethodBuilderAttribute(Type builderType)
{
BuilderType = builderType;
}
/// <summary>Gets the <see cref="Type"/> of the associated builder.</summary>
public Type BuilderType { get; }
}
}

View File

@@ -0,0 +1,105 @@
// 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.Runtime.InteropServices;
using System.Security;
using System.Threading.Tasks;
namespace System.Runtime.CompilerServices
{
/// <summary>Represents a builder for asynchronous methods that returns a <see cref="ValueTask{TResult}"/>.</summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
[StructLayout(LayoutKind.Auto)]
public struct AsyncValueTaskMethodBuilder<TResult>
{
/// <summary>The <see cref="AsyncTaskMethodBuilder{TResult}"/> to which most operations are delegated.</summary>
private AsyncTaskMethodBuilder<TResult> _methodBuilder;
/// <summary>The result for this builder, if it's completed before any awaits occur.</summary>
private TResult _result;
/// <summary>true if <see cref="_result"/> contains the synchronous result for the async method; otherwise, false.</summary>
private bool _haveResult;
/// <summary>true if the builder should be used for setting/getting the result; otherwise, false.</summary>
private bool _useBuilder;
/// <summary>Creates an instance of the <see cref="AsyncValueTaskMethodBuilder{TResult}"/> struct.</summary>
/// <returns>The initialized instance.</returns>
public static AsyncValueTaskMethodBuilder<TResult> Create() =>
new AsyncValueTaskMethodBuilder<TResult>() { _methodBuilder = AsyncTaskMethodBuilder<TResult>.Create() };
/// <summary>Begins running the builder with the associated state machine.</summary>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="stateMachine">The state machine instance, passed by reference.</param>
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
{
_methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics
}
/// <summary>Associates the builder with the specified state machine.</summary>
/// <param name="stateMachine">The state machine instance to associate with the builder.</param>
public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine);
/// <summary>Marks the task as successfully completed.</summary>
/// <param name="result">The result to use to complete the task.</param>
public void SetResult(TResult result)
{
if (_useBuilder)
{
_methodBuilder.SetResult(result);
}
else
{
_result = result;
_haveResult = true;
}
}
/// <summary>Marks the task as failed and binds the specified exception to the task.</summary>
/// <param name="exception">The exception to bind to the task.</param>
public void SetException(Exception exception) => _methodBuilder.SetException(exception);
/// <summary>Gets the task for this builder.</summary>
public ValueTask<TResult> Task
{
get
{
if (_haveResult)
{
return new ValueTask<TResult>(_result);
}
else
{
_useBuilder = true;
return new ValueTask<TResult>(_methodBuilder.Task);
}
}
}
/// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
/// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="awaiter">the awaiter</param>
/// <param name="stateMachine">The state machine.</param>
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
}
/// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
/// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="awaiter">the awaiter</param>
/// <param name="stateMachine">The state machine.</param>
[SecuritySafeCritical]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
}
}
}

View File

@@ -0,0 +1,79 @@
// 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.Runtime.InteropServices;
using System.Threading.Tasks;
namespace System.Runtime.CompilerServices
{
/// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask{TResult}"/>.</summary>
/// <typeparam name="TResult">The type of the result produced.</typeparam>
[StructLayout(LayoutKind.Auto)]
public struct ConfiguredValueTaskAwaitable<TResult>
{
/// <summary>The wrapped <see cref="ValueTask{TResult}"/>.</summary>
private readonly ValueTask<TResult> _value;
/// <summary>true to attempt to marshal the continuation back to the original context captured; otherwise, false.</summary>
private readonly bool _continueOnCapturedContext;
/// <summary>Initializes the awaitable.</summary>
/// <param name="value">The wrapped <see cref="ValueTask{TResult}"/>.</param>
/// <param name="continueOnCapturedContext">
/// true to attempt to marshal the continuation back to the original synchronization context captured; otherwise, false.
/// </param>
internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value, bool continueOnCapturedContext)
{
_value = value;
_continueOnCapturedContext = continueOnCapturedContext;
}
/// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable{TResult}"/> instance.</summary>
public ConfiguredValueTaskAwaiter GetAwaiter()
{
return new ConfiguredValueTaskAwaiter(_value, _continueOnCapturedContext);
}
/// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
[StructLayout(LayoutKind.Auto)]
public struct ConfiguredValueTaskAwaiter : 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>
internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value, bool continueOnCapturedContext)
{
_value = value;
_continueOnCapturedContext = continueOnCapturedContext;
}
/// <summary>Gets whether the <see cref="ConfiguredValueTaskAwaitable{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 the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
public void OnCompleted(Action continuation)
{
_value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
/// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
public void UnsafeOnCompleted(Action continuation)
{
_value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
}
}
}

View File

@@ -0,0 +1,43 @@
// 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.Runtime.InteropServices;
using System.Threading.Tasks;
namespace System.Runtime.CompilerServices
{
/// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
public struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
{
/// <summary>The value being awaited.</summary>
private readonly ValueTask<TResult> _value;
/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
internal ValueTaskAwaiter(ValueTask<TResult> value) { _value = value; }
/// <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: true).GetAwaiter().OnCompleted(continuation);
}
/// <summary>Schedules the continuation action for this ValueTask.</summary>
public void UnsafeOnCompleted(Action continuation)
{
_value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation);
}
}
}

View File

@@ -0,0 +1,187 @@
// 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)
{
throw new ArgumentNullException(nameof(task));
}
_task = task;
_result = default(TResult);
}
/// <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);
}
/// <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)
{
return new ConfiguredValueTaskAwaitable<TResult>(this, continueOnCapturedContext: continueOnCapturedContext);
}
/// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
public override string ToString()
{
if (_task != null)
{
return _task.Status == TaskStatus.RanToCompletion && _task.Result != null ?
_task.Result.ToString() :
string.Empty;
}
else
{
return _result != null ?
_result.ToString() :
string.Empty;
}
}
// TODO: 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();
}
}

View File

@@ -0,0 +1,40 @@
// 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 Xunit;
namespace System.Runtime.CompilerServices.Tests
{
public class AsyncMethodBuilderAttributeTests
{
[Theory]
[InlineData(typeof(string))]
[InlineData(typeof(int))]
[InlineData(typeof(AsyncValueTaskMethodBuilder<>))]
[InlineData(typeof(AsyncValueTaskMethodBuilder<int>))]
[InlineData(typeof(AsyncValueTaskMethodBuilder<string>))]
public void Ctor_BuilderType_Roundtrip(Type builderType)
{
var amba = new AsyncMethodBuilderAttribute(builderType);
Assert.Same(builderType, amba.BuilderType);
}
// No tests for the following, other than verifying that they successfully compile
[AsyncMethodBuilder(typeof(string))]
class MyClass { }
[AsyncMethodBuilder(typeof(string))]
struct MyStruct { }
[AsyncMethodBuilder(typeof(string))]
interface MyInterface { }
[AsyncMethodBuilder(typeof(string))]
enum MyEnum { }
[AsyncMethodBuilder(typeof(string))]
delegate void MyDelegate();
}
}

View File

@@ -0,0 +1,219 @@
// 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.Runtime.CompilerServices;
using Xunit;
namespace System.Threading.Tasks.Tests
{
public class AsyncValueTaskMethodBuilderTests
{
[Fact]
public void Create_ReturnsDefaultInstance()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
Assert.Equal(default(AsyncValueTaskMethodBuilder<int>), b); // implementation detail being verified
}
[Fact]
public void SetResult_BeforeAccessTask_ValueTaskContainsValue()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
b.SetResult(42);
ValueTask<int> vt = b.Task;
Assert.True(vt.IsCompletedSuccessfully);
Assert.False(WrapsTask(vt));
Assert.Equal(42, vt.Result);
}
[Fact]
public void SetResult_AfterAccessTask_ValueTaskContainsValue()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
ValueTask<int> vt = b.Task;
b.SetResult(42);
Assert.True(vt.IsCompletedSuccessfully);
Assert.True(WrapsTask(vt));
Assert.Equal(42, vt.Result);
}
[Fact]
public void SetException_BeforeAccessTask_FaultsTask()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
var e = new FormatException();
b.SetException(e);
ValueTask<int> vt = b.Task;
Assert.True(vt.IsFaulted);
Assert.Same(e, Assert.Throws<FormatException>(() => vt.GetAwaiter().GetResult()));
}
[Fact]
public void SetException_AfterAccessTask_FaultsTask()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
var e = new FormatException();
ValueTask<int> vt = b.Task;
b.SetException(e);
Assert.True(vt.IsFaulted);
Assert.Same(e, Assert.Throws<FormatException>(() => vt.GetAwaiter().GetResult()));
}
[Fact]
public void SetException_OperationCanceledException_CancelsTask()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
var e = new OperationCanceledException();
ValueTask<int> vt = b.Task;
b.SetException(e);
Assert.True(vt.IsCanceled);
Assert.Same(e, Assert.Throws<OperationCanceledException>(() => vt.GetAwaiter().GetResult()));
}
[Fact]
public void Start_InvokesMoveNext()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
int invokes = 0;
var dsm = new DelegateStateMachine { MoveNextDelegate = () => invokes++ };
b.Start(ref dsm);
Assert.Equal(1, invokes);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task AwaitOnCompleted_InvokesStateMachineMethods(bool awaitUnsafe)
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
var ignored = b.Task;
var callbackCompleted = new TaskCompletionSource<bool>();
IAsyncStateMachine foundSm = null;
var dsm = new DelegateStateMachine
{
MoveNextDelegate = () => callbackCompleted.SetResult(true),
SetStateMachineDelegate = sm => foundSm = sm
};
TaskAwaiter t = Task.CompletedTask.GetAwaiter();
if (awaitUnsafe)
{
b.AwaitUnsafeOnCompleted(ref t, ref dsm);
}
else
{
b.AwaitOnCompleted(ref t, ref dsm);
}
await callbackCompleted.Task;
Assert.Equal(dsm, foundSm);
}
[Theory]
[InlineData(1, false)]
[InlineData(2, false)]
[InlineData(1, true)]
[InlineData(2, true)]
public void AwaitOnCompleted_ForcesTaskCreation(int numAwaits, bool awaitUnsafe)
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
var dsm = new DelegateStateMachine();
TaskAwaiter<int> t = new TaskCompletionSource<int>().Task.GetAwaiter();
Assert.InRange(numAwaits, 1, int.MaxValue);
for (int i = 1; i <= numAwaits; i++)
{
if (awaitUnsafe)
{
b.AwaitUnsafeOnCompleted(ref t, ref dsm);
}
else
{
b.AwaitOnCompleted(ref t, ref dsm);
}
}
b.SetResult(42);
Assert.True(WrapsTask(b.Task));
Assert.Equal(42, b.Task.Result);
}
[Fact]
public void SetStateMachine_InvalidArgument_ThrowsException()
{
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
Assert.Throws<ArgumentNullException>("stateMachine", () => b.SetStateMachine(null));
b.SetStateMachine(new DelegateStateMachine());
}
[Fact]
public void Start_ExecutionContextChangesInMoveNextDontFlowOut()
{
var al = new AsyncLocal<int> { Value = 0 };
int calls = 0;
var dsm = new DelegateStateMachine
{
MoveNextDelegate = () =>
{
al.Value++;
calls++;
}
};
dsm.MoveNext();
Assert.Equal(1, al.Value);
Assert.Equal(1, calls);
dsm.MoveNext();
Assert.Equal(2, al.Value);
Assert.Equal(2, calls);
AsyncValueTaskMethodBuilder<int> b = ValueTask<int>.CreateAsyncMethodBuilder();
b.Start(ref dsm);
Assert.Equal(2, al.Value); // change should not be visible
Assert.Equal(3, calls);
// Make sure we've not caused the Task to be allocated
b.SetResult(42);
ValueTask<int> vt = b.Task;
Assert.False(WrapsTask(vt));
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(10)]
public static async Task UsedWithAsyncMethod_CompletesSuccessfully(int yields)
{
Assert.Equal(42, await ValueTaskReturningAsyncMethod(42));
ValueTask<int> vt = ValueTaskReturningAsyncMethod(84);
Assert.Equal(yields > 0, WrapsTask(vt));
Assert.Equal(84, await vt);
async ValueTask<int> ValueTaskReturningAsyncMethod(int result)
{
for (int i = 0; i < yields; i++) await Task.Yield();
return result;
}
}
/// <summary>Gets whether the ValueTask has a non-null Task.</summary>
private static bool WrapsTask<T>(ValueTask<T> vt) => ReferenceEquals(vt.AsTask(), vt.AsTask());
private struct DelegateStateMachine : IAsyncStateMachine
{
internal Action MoveNextDelegate;
public void MoveNext() => MoveNextDelegate?.Invoke();
internal Action<IAsyncStateMachine> SetStateMachineDelegate;
public void SetStateMachine(IAsyncStateMachine stateMachine) => SetStateMachineDelegate?.Invoke(stateMachine);
}
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
netstandard1.3;
</BuildConfigurations>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<ItemGroup>
<Project Include="System.Threading.Tasks.Extensions.Tests.csproj" />
<Project Include="System.Threading.Tasks.Extensions.Tests.csproj">
<OSGroup>Windows_NT</OSGroup>
<TestTFMs>netcore50;net46</TestTFMs>
</Project>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.traversal.targets))\dir.traversal.targets" />
</Project>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<ProjectGuid>{82B54697-0251-47A1-8546-FC507D0F3B08}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netstandard1.3-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netstandard1.3-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="AsyncMethodBuilderAttributeTests.cs" />
<Compile Include="AsyncValueTaskMethodBuilderTests.cs" />
<Compile Include="ValueTaskTests.cs" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>

View File

@@ -0,0 +1,342 @@
// 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.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Xunit;
namespace System.Threading.Tasks.Tests
{
public class ValueTaskTests
{
[Fact]
public void DefaultValueTask_ValueType_DefaultValue()
{
Assert.True(default(ValueTask<int>).IsCompleted);
Assert.True(default(ValueTask<int>).IsCompletedSuccessfully);
Assert.False(default(ValueTask<int>).IsFaulted);
Assert.False(default(ValueTask<int>).IsCanceled);
Assert.Equal(0, default(ValueTask<int>).Result);
Assert.True(default(ValueTask<string>).IsCompleted);
Assert.True(default(ValueTask<string>).IsCompletedSuccessfully);
Assert.False(default(ValueTask<string>).IsFaulted);
Assert.False(default(ValueTask<string>).IsCanceled);
Assert.Equal(null, default(ValueTask<string>).Result);
}
[Fact]
public void CreateFromValue_IsRanToCompletion()
{
ValueTask<int> t = new ValueTask<int>(42);
Assert.True(t.IsCompleted);
Assert.True(t.IsCompletedSuccessfully);
Assert.False(t.IsFaulted);
Assert.False(t.IsCanceled);
Assert.Equal(42, t.Result);
}
[Fact]
public void CreateFromCompletedTask_IsRanToCompletion()
{
ValueTask<int> t = new ValueTask<int>(Task.FromResult(42));
Assert.True(t.IsCompleted);
Assert.True(t.IsCompletedSuccessfully);
Assert.False(t.IsFaulted);
Assert.False(t.IsCanceled);
Assert.Equal(42, t.Result);
}
[Fact]
public void CreateFromNotCompletedTask_IsNotRanToCompletion()
{
var tcs = new TaskCompletionSource<int>();
ValueTask<int> t = new ValueTask<int>(tcs.Task);
Assert.False(t.IsCompleted);
Assert.False(t.IsCompletedSuccessfully);
Assert.False(t.IsFaulted);
Assert.False(t.IsCanceled);
tcs.SetResult(42);
Assert.Equal(42, t.Result);
Assert.True(t.IsCompleted);
Assert.True(t.IsCompletedSuccessfully);
Assert.False(t.IsFaulted);
Assert.False(t.IsCanceled);
}
[Fact]
public void CreateFromNullTask_Throws()
{
Assert.Throws<ArgumentNullException>(() => new ValueTask<int>((Task<int>)null));
Assert.Throws<ArgumentNullException>(() => new ValueTask<string>((Task<string>)null));
}
[Fact]
public void CreateFromTask_AsTaskIdempotent()
{
Task<int> source = Task.FromResult(42);
ValueTask<int> t = new ValueTask<int>(source);
Assert.Same(source, t.AsTask());
Assert.Same(t.AsTask(), t.AsTask());
}
[Fact]
public void CreateFromValue_AsTaskNotIdempotent()
{
ValueTask<int> t = new ValueTask<int>(42);
Assert.NotSame(Task.FromResult(42), t.AsTask());
Assert.NotSame(t.AsTask(), t.AsTask());
}
[Fact]
public async Task CreateFromValue_Await()
{
ValueTask<int> t = new ValueTask<int>(42);
Assert.Equal(42, await t);
Assert.Equal(42, await t.ConfigureAwait(false));
Assert.Equal(42, await t.ConfigureAwait(true));
}
[Fact]
public async Task CreateFromTask_Await_Normal()
{
Task<int> source = Task.Delay(1).ContinueWith(_ => 42);
ValueTask<int> t = new ValueTask<int>(source);
Assert.Equal(42, await t);
}
[Fact]
public async Task CreateFromTask_Await_ConfigureAwaitFalse()
{
Task<int> source = Task.Delay(1).ContinueWith(_ => 42);
ValueTask<int> t = new ValueTask<int>(source);
Assert.Equal(42, await t.ConfigureAwait(false));
}
[Fact]
public async Task CreateFromTask_Await_ConfigureAwaitTrue()
{
Task<int> source = Task.Delay(1).ContinueWith(_ => 42);
ValueTask<int> t = new ValueTask<int>(source);
Assert.Equal(42, await t.ConfigureAwait(true));
}
[Fact]
public async Task Awaiter_OnCompleted()
{
// Since ValueTask implements both OnCompleted and UnsafeOnCompleted,
// OnCompleted typically won't be used by await, so we add an explicit test
// for it here.
ValueTask<int> t = new ValueTask<int>(42);
var tcs = new TaskCompletionSource<bool>();
t.GetAwaiter().OnCompleted(() => tcs.SetResult(true));
await tcs.Task;
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task ConfiguredAwaiter_OnCompleted(bool continueOnCapturedContext)
{
// Since ValueTask implements both OnCompleted and UnsafeOnCompleted,
// OnCompleted typically won't be used by await, so we add an explicit test
// for it here.
ValueTask<int> t = new ValueTask<int>(42);
var tcs = new TaskCompletionSource<bool>();
t.ConfigureAwait(continueOnCapturedContext).GetAwaiter().OnCompleted(() => tcs.SetResult(true));
await tcs.Task;
}
[Fact]
public async Task Awaiter_ContinuesOnCapturedContext()
{
await Task.Run(() =>
{
var tsc = new TrackingSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(tsc);
try
{
ValueTask<int> t = new ValueTask<int>(42);
var mres = new ManualResetEventSlim();
t.GetAwaiter().OnCompleted(() => mres.Set());
Assert.True(mres.Wait(10000));
Assert.Equal(1, tsc.Posts);
}
finally
{
SynchronizationContext.SetSynchronizationContext(null);
}
});
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task ConfiguredAwaiter_ContinuesOnCapturedContext(bool continueOnCapturedContext)
{
await Task.Run(() =>
{
var tsc = new TrackingSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(tsc);
try
{
ValueTask<int> t = new ValueTask<int>(42);
var mres = new ManualResetEventSlim();
t.ConfigureAwait(continueOnCapturedContext).GetAwaiter().OnCompleted(() => mres.Set());
Assert.True(mres.Wait(10000));
Assert.Equal(continueOnCapturedContext ? 1 : 0, tsc.Posts);
}
finally
{
SynchronizationContext.SetSynchronizationContext(null);
}
});
}
[Fact]
public void GetHashCode_ContainsResult()
{
ValueTask<int> t = new ValueTask<int>(42);
Assert.Equal(t.Result.GetHashCode(), t.GetHashCode());
}
[Fact]
public void GetHashCode_ContainsTask()
{
ValueTask<string> t = new ValueTask<string>(Task.FromResult("42"));
Assert.Equal(t.AsTask().GetHashCode(), t.GetHashCode());
}
[Fact]
public void GetHashCode_ContainsNull()
{
ValueTask<string> t = new ValueTask<string>((string)null);
Assert.Equal(0, t.GetHashCode());
}
[Fact]
public void OperatorEquals()
{
Assert.True(new ValueTask<int>(42) == new ValueTask<int>(42));
Assert.False(new ValueTask<int>(42) == new ValueTask<int>(43));
Assert.True(new ValueTask<string>("42") == new ValueTask<string>("42"));
Assert.True(new ValueTask<string>((string)null) == new ValueTask<string>((string)null));
Assert.False(new ValueTask<string>("42") == new ValueTask<string>((string)null));
Assert.False(new ValueTask<string>((string)null) == new ValueTask<string>("42"));
Assert.False(new ValueTask<int>(42) == new ValueTask<int>(Task.FromResult(42)));
Assert.False(new ValueTask<int>(Task.FromResult(42)) == new ValueTask<int>(42));
}
[Fact]
public void OperatorNotEquals()
{
Assert.False(new ValueTask<int>(42) != new ValueTask<int>(42));
Assert.True(new ValueTask<int>(42) != new ValueTask<int>(43));
Assert.False(new ValueTask<string>("42") != new ValueTask<string>("42"));
Assert.False(new ValueTask<string>((string)null) != new ValueTask<string>((string)null));
Assert.True(new ValueTask<string>("42") != new ValueTask<string>((string)null));
Assert.True(new ValueTask<string>((string)null) != new ValueTask<string>("42"));
Assert.True(new ValueTask<int>(42) != new ValueTask<int>(Task.FromResult(42)));
Assert.True(new ValueTask<int>(Task.FromResult(42)) != new ValueTask<int>(42));
}
[Fact]
public void Equals_ValueTask()
{
Assert.True(new ValueTask<int>(42).Equals(new ValueTask<int>(42)));
Assert.False(new ValueTask<int>(42).Equals(new ValueTask<int>(43)));
Assert.True(new ValueTask<string>("42").Equals(new ValueTask<string>("42")));
Assert.True(new ValueTask<string>((string)null).Equals(new ValueTask<string>((string)null)));
Assert.False(new ValueTask<string>("42").Equals(new ValueTask<string>((string)null)));
Assert.False(new ValueTask<string>((string)null).Equals(new ValueTask<string>("42")));
Assert.False(new ValueTask<int>(42).Equals(new ValueTask<int>(Task.FromResult(42))));
Assert.False(new ValueTask<int>(Task.FromResult(42)).Equals(new ValueTask<int>(42)));
}
[Fact]
public void Equals_Object()
{
Assert.True(new ValueTask<int>(42).Equals((object)new ValueTask<int>(42)));
Assert.False(new ValueTask<int>(42).Equals((object)new ValueTask<int>(43)));
Assert.True(new ValueTask<string>("42").Equals((object)new ValueTask<string>("42")));
Assert.True(new ValueTask<string>((string)null).Equals((object)new ValueTask<string>((string)null)));
Assert.False(new ValueTask<string>("42").Equals((object)new ValueTask<string>((string)null)));
Assert.False(new ValueTask<string>((string)null).Equals((object)new ValueTask<string>("42")));
Assert.False(new ValueTask<int>(42).Equals((object)new ValueTask<int>(Task.FromResult(42))));
Assert.False(new ValueTask<int>(Task.FromResult(42)).Equals((object)new ValueTask<int>(42)));
Assert.False(new ValueTask<int>(42).Equals((object)null));
Assert.False(new ValueTask<int>(42).Equals(new object()));
Assert.False(new ValueTask<int>(42).Equals((object)42));
}
[Fact]
public void ToString_Success()
{
Assert.Equal("Hello", new ValueTask<string>("Hello").ToString());
Assert.Equal("Hello", new ValueTask<string>(Task.FromResult("Hello")).ToString());
Assert.Equal("42", new ValueTask<int>(42).ToString());
Assert.Equal("42", new ValueTask<int>(Task.FromResult(42)).ToString());
Assert.Same(string.Empty, new ValueTask<string>(string.Empty).ToString());
Assert.Same(string.Empty, new ValueTask<string>(Task.FromResult(string.Empty)).ToString());
Assert.Same(string.Empty, new ValueTask<string>(Task.FromException<string>(new InvalidOperationException())).ToString());
Assert.Same(string.Empty, new ValueTask<string>(Task.FromException<string>(new OperationCanceledException())).ToString());
Assert.Same(string.Empty, new ValueTask<string>(Task.FromCanceled<string>(new CancellationToken(true))).ToString());
Assert.Equal("0", default(ValueTask<int>).ToString());
Assert.Same(string.Empty, default(ValueTask<string>).ToString());
Assert.Same(string.Empty, new ValueTask<string>((string)null).ToString());
Assert.Same(string.Empty, new ValueTask<string>(Task.FromResult<string>(null)).ToString());
Assert.Same(string.Empty, new ValueTask<DateTime>(new TaskCompletionSource<DateTime>().Task).ToString());
}
[Theory]
[InlineData(typeof(ValueTask<>))]
[InlineData(typeof(ValueTask<int>))]
[InlineData(typeof(ValueTask<string>))]
public void AsyncMethodBuilderAttribute_ValueTaskAttributed(Type valueTaskType)
{
CustomAttributeData cad = valueTaskType.GetTypeInfo().CustomAttributes.Single(attr => attr.AttributeType == typeof(AsyncMethodBuilderAttribute));
Type builderTypeCtorArg = (Type)cad.ConstructorArguments[0].Value;
Assert.Equal(typeof(AsyncValueTaskMethodBuilder<>), builderTypeCtorArg);
AsyncMethodBuilderAttribute amba = valueTaskType.GetTypeInfo().GetCustomAttribute<AsyncMethodBuilderAttribute>();
Assert.Equal(builderTypeCtorArg, amba.BuilderType);
}
private sealed class TrackingSynchronizationContext : SynchronizationContext
{
internal int Posts { get; set; }
public override void Post(SendOrPostCallback d, object state)
{
Posts++;
base.Post(d, state);
}
}
}
}