/
AsyncTaskMethodBuilder.cs
143 lines (130 loc) · 7.79 KB
/
AsyncTaskMethodBuilder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Threading.Tasks;
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Provides a builder for asynchronous methods that return <see cref="Threading.Tasks.Task"/>.
/// This type is intended for compiler use only.
/// </summary>
/// <remarks>
/// AsyncTaskMethodBuilder is a value type, and thus it is copied by value.
/// Prior to being copied, one of its Task, SetResult, or SetException members must be accessed,
/// or else the copies may end up building distinct Task instances.
/// </remarks>
public struct AsyncTaskMethodBuilder
{
/// <summary>The lazily-initialized built task.</summary>
private Task<VoidTaskResult>? m_task; // Debugger depends on the exact name of this field.
/// <summary>Initializes a new <see cref="AsyncTaskMethodBuilder"/>.</summary>
/// <returns>The initialized <see cref="AsyncTaskMethodBuilder"/>.</returns>
public static AsyncTaskMethodBuilder Create() => default;
/// <summary>Initiates the builder's execution with the associated state machine.</summary>
/// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
/// <param name="stateMachine">The state machine instance, passed by reference.</param>
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
AsyncMethodBuilderCore.Start(ref stateMachine);
/// <summary>Associates the builder with the state machine it represents.</summary>
/// <param name="stateMachine">The heap-allocated state machine object.</param>
/// <exception cref="ArgumentNullException">The <paramref name="stateMachine"/> argument was null (<see langword="Nothing" /> in Visual Basic).</exception>
/// <exception cref="InvalidOperationException">The builder is incorrectly initialized.</exception>
public void SetStateMachine(IAsyncStateMachine stateMachine) =>
AsyncMethodBuilderCore.SetStateMachine(stateMachine, task: null);
/// <summary>
/// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
/// </summary>
/// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
/// <typeparam name="TStateMachine">Specifies 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 =>
AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted(ref awaiter, ref stateMachine, ref m_task);
/// <summary>
/// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
/// </summary>
/// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
/// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
/// <param name="awaiter">The awaiter.</param>
/// <param name="stateMachine">The state machine.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine =>
AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine, ref m_task);
/// <summary>Gets the <see cref="Threading.Tasks.Task"/> for this builder.</summary>
/// <returns>The <see cref="Threading.Tasks.Task"/> representing the builder's asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">The builder is not initialized.</exception>
public Task Task
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => m_task ?? InitializeTaskAsPromise();
}
/// <summary>
/// Initializes the task, which must not yet be initialized. Used only when the Task is being forced into
/// existence when no state machine is needed, e.g. when the builder is being synchronously completed with
/// an exception, when the builder is being used out of the context of an async method, etc.
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
private Task<VoidTaskResult> InitializeTaskAsPromise()
{
Debug.Assert(m_task == null);
return m_task = new Task<VoidTaskResult>();
}
/// <summary>
/// Completes the <see cref="Threading.Tasks.Task"/> in the
/// <see cref="TaskStatus">RanToCompletion</see> state.
/// </summary>
/// <exception cref="InvalidOperationException">The builder is not initialized.</exception>
/// <exception cref="InvalidOperationException">The task has already completed.</exception>
public void SetResult()
{
// Get the currently stored task, which will be non-null if get_Task has already been accessed.
// If there isn't one, store the supplied completed task.
if (m_task is null)
{
m_task = Task.s_cachedCompleted;
}
else
{
// Otherwise, complete the task that's there.
AsyncTaskMethodBuilder<VoidTaskResult>.SetExistingTaskResult(m_task, default!);
}
}
/// <summary>
/// Completes the <see cref="Threading.Tasks.Task"/> in the
/// <see cref="TaskStatus">Faulted</see> state with the specified exception.
/// </summary>
/// <param name="exception">The <see cref="Exception"/> to use to fault the task.</param>
/// <exception cref="ArgumentNullException">The <paramref name="exception"/> argument is null (<see langword="Nothing" /> in Visual Basic).</exception>
/// <exception cref="InvalidOperationException">The builder is not initialized.</exception>
/// <exception cref="InvalidOperationException">The task has already completed.</exception>
public void SetException(Exception exception) =>
AsyncTaskMethodBuilder<VoidTaskResult>.SetException(exception, ref m_task);
/// <summary>
/// Called by the debugger to request notification when the first wait operation
/// (await, Wait, Result, etc.) on this builder's task completes.
/// </summary>
/// <param name="enabled">
/// true to enable notification; false to disable a previously set notification.
/// </param>
internal void SetNotificationForWaitCompletion(bool enabled) =>
AsyncTaskMethodBuilder<VoidTaskResult>.SetNotificationForWaitCompletion(enabled, ref m_task);
/// <summary>
/// Gets an object that may be used to uniquely identify this builder to the debugger.
/// </summary>
/// <remarks>
/// This property lazily instantiates the ID in a non-thread-safe manner.
/// It must only be used by the debugger and tracing purposes, and only in a single-threaded manner
/// when no other threads are in the middle of accessing this property or this.Task.
/// </remarks>
internal object ObjectIdForDebugger =>
m_task ??= AsyncTaskMethodBuilder<VoidTaskResult>.CreateWeaklyTypedStateMachineBox();
}
}