1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System
.Diagnostics
;
6 using System
.Threading
;
7 using System
.Threading
.Tasks
;
9 namespace System
.Runtime
.CompilerServices
12 /// Provides a builder for asynchronous methods that return void.
13 /// This type is intended for compiler use only.
15 public struct AsyncVoidMethodBuilder
17 /// <summary>The synchronization context associated with this operation.</summary>
18 private SynchronizationContext
? _synchronizationContext
;
19 /// <summary>The builder this void builder wraps.</summary>
20 private AsyncTaskMethodBuilder _builder
; // mutable struct: must not be readonly
22 /// <summary>Initializes a new <see cref="AsyncVoidMethodBuilder"/>.</summary>
23 /// <returns>The initialized <see cref="AsyncVoidMethodBuilder"/>.</returns>
24 public static AsyncVoidMethodBuilder
Create()
26 SynchronizationContext
? sc
= SynchronizationContext
.Current
;
27 sc
?.OperationStarted();
29 // _builder should be initialized to AsyncTaskMethodBuilder.Create(), but on coreclr
30 // that Create() is a nop, so we can just return the default here.
31 return new AsyncVoidMethodBuilder() { _synchronizationContext = sc }
;
34 /// <summary>Initiates the builder's execution with the associated state machine.</summary>
35 /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
36 /// <param name="stateMachine">The state machine instance, passed by reference.</param>
37 /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception>
39 [MethodImpl(MethodImplOptions
.AggressiveInlining
)]
40 public void Start
<TStateMachine
>(ref TStateMachine stateMachine
) where TStateMachine
: IAsyncStateMachine
=>
41 AsyncMethodBuilderCore
.Start(ref stateMachine
);
43 /// <summary>Associates the builder with the state machine it represents.</summary>
44 /// <param name="stateMachine">The heap-allocated state machine object.</param>
45 /// <exception cref="System.ArgumentNullException">The <paramref name="stateMachine"/> argument was null (Nothing in Visual Basic).</exception>
46 /// <exception cref="System.InvalidOperationException">The builder is incorrectly initialized.</exception>
47 public void SetStateMachine(IAsyncStateMachine stateMachine
) =>
48 _builder
.SetStateMachine(stateMachine
);
51 /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
53 /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
54 /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
55 /// <param name="awaiter">The awaiter.</param>
56 /// <param name="stateMachine">The state machine.</param>
57 public void AwaitOnCompleted
<TAwaiter
, TStateMachine
>(
58 ref TAwaiter awaiter
, ref TStateMachine stateMachine
)
59 where TAwaiter
: INotifyCompletion
60 where TStateMachine
: IAsyncStateMachine
=>
61 _builder
.AwaitOnCompleted(ref awaiter
, ref stateMachine
);
64 /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
66 /// <typeparam name="TAwaiter">Specifies the type of the awaiter.</typeparam>
67 /// <typeparam name="TStateMachine">Specifies the type of the state machine.</typeparam>
68 /// <param name="awaiter">The awaiter.</param>
69 /// <param name="stateMachine">The state machine.</param>
70 public void AwaitUnsafeOnCompleted
<TAwaiter
, TStateMachine
>(
71 ref TAwaiter awaiter
, ref TStateMachine stateMachine
)
72 where TAwaiter
: ICriticalNotifyCompletion
73 where TStateMachine
: IAsyncStateMachine
=>
74 _builder
.AwaitUnsafeOnCompleted(ref awaiter
, ref stateMachine
);
76 /// <summary>Completes the method builder successfully.</summary>
77 public void SetResult()
79 if (AsyncCausalityTracer
.LoggingOn
)
81 AsyncCausalityTracer
.TraceOperationCompletion(this.Task
, AsyncCausalityStatus
.Completed
);
84 // Mark the builder as completed. As this is a void-returning method, this mostly
85 // doesn't matter, but it can affect things like debug events related to finalization.
88 if (_synchronizationContext
!= null)
90 NotifySynchronizationContextOfCompletion();
94 /// <summary>Faults the method builder with an exception.</summary>
95 /// <param name="exception">The exception that is the cause of this fault.</param>
96 /// <exception cref="System.ArgumentNullException">The <paramref name="exception"/> argument is null (Nothing in Visual Basic).</exception>
97 /// <exception cref="System.InvalidOperationException">The builder is not initialized.</exception>
98 public void SetException(Exception exception
)
100 if (exception
== null)
102 ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.exception
);
105 if (AsyncCausalityTracer
.LoggingOn
)
107 AsyncCausalityTracer
.TraceOperationCompletion(this.Task
, AsyncCausalityStatus
.Error
);
110 if (_synchronizationContext
!= null)
112 // If we captured a synchronization context, Post the throwing of the exception to it
113 // and decrement its outstanding operation count.
116 System
.Threading
.Tasks
.Task
.ThrowAsync(exception
, targetContext
: _synchronizationContext
);
120 NotifySynchronizationContextOfCompletion();
125 // Otherwise, queue the exception to be thrown on the ThreadPool. This will
126 // result in a crash unless legacy exception behavior is enabled by a config
127 // file or a CLR host.
128 System
.Threading
.Tasks
.Task
.ThrowAsync(exception
, targetContext
: null);
131 // The exception was propagated already; we don't need or want to fault the builder, just mark it as completed.
132 _builder
.SetResult();
135 /// <summary>Notifies the current synchronization context that the operation completed.</summary>
136 private void NotifySynchronizationContextOfCompletion()
138 Debug
.Assert(_synchronizationContext
!= null, "Must only be used with a non-null context.");
141 _synchronizationContext
.OperationCompleted();
143 catch (Exception exc
)
145 // If the interaction with the SynchronizationContext goes awry,
146 // fall back to propagating on the ThreadPool.
147 Task
.ThrowAsync(exc
, targetContext
: null);
151 /// <summary>Lazily instantiate the Task in a non-thread-safe manner.</summary>
152 private Task Task
=> _builder
.Task
;
155 /// Gets an object that may be used to uniquely identify this builder to the debugger.
158 /// This property lazily instantiates the ID in a non-thread-safe manner.
159 /// It must only be used by the debugger and AsyncCausalityTracer in a single-threaded manner.
161 internal object ObjectIdForDebugger
=> _builder
.ObjectIdForDebugger
;