Separate Async*MethodBuilder types into their own files (#26364)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Runtime / CompilerServices / AsyncVoidMethodBuilder.cs
blob3a0a0a2c63093b3d7ec9a932e189f68c72d39464
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
11 /// <summary>
12 /// Provides a builder for asynchronous methods that return void.
13 /// This type is intended for compiler use only.
14 /// </summary>
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>
38 [DebuggerStepThrough]
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);
50 /// <summary>
51 /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
52 /// </summary>
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);
63 /// <summary>
64 /// Schedules the specified state machine to be pushed forward when the specified awaiter completes.
65 /// </summary>
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.
86 _builder.SetResult();
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);
118 finally
120 NotifySynchronizationContextOfCompletion();
123 else
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;
154 /// <summary>
155 /// Gets an object that may be used to uniquely identify this builder to the debugger.
156 /// </summary>
157 /// <remarks>
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.
160 /// </remarks>
161 internal object ObjectIdForDebugger => _builder.ObjectIdForDebugger;