@@ -157,6 +157,8 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
157157 {
158158 ExecutionContext ? currentContext = ExecutionContext . Capture ( ) ;
159159
160+ IAsyncStateMachineBox result ;
161+
160162 // Check first for the most common case: not the first yield in an async method.
161163 // In this case, the first yield will have already "boxed" the state machine in
162164 // a strongly-typed manner into an AsyncStateMachineBox. It will already contain
@@ -168,9 +170,8 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
168170 {
169171 stronglyTypedBox . Context = currentContext ;
170172 }
171- return stronglyTypedBox ;
173+ result = stronglyTypedBox ;
172174 }
173-
174175 // The least common case: we have a weakly-typed boxed. This results if the debugger
175176 // or some other use of reflection accesses a property like ObjectIdForDebugger or a
176177 // method like SetNotificationForWaitCompletion prior to the first await happening. In
@@ -180,7 +181,7 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
180181 // result in a boxing allocation when storing the TStateMachine if it's a struct, but
181182 // this only happens in active debugging scenarios where such performance impact doesn't
182183 // matter.
183- if ( taskField is AsyncStateMachineBox < IAsyncStateMachine > weaklyTypedBox )
184+ else if ( taskField is AsyncStateMachineBox < IAsyncStateMachine > weaklyTypedBox )
184185 {
185186 // If this is the first await, we won't yet have a state machine, so store it.
186187 if ( weaklyTypedBox . StateMachine == null )
@@ -192,52 +193,55 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
192193 // Update the context. This only happens with a debugger, so no need to spend
193194 // extra IL checking for equality before doing the assignment.
194195 weaklyTypedBox . Context = currentContext ;
195- return weaklyTypedBox ;
196+ result = weaklyTypedBox ;
196197 }
197-
198- // Alert a listening debugger that we can't make forward progress unless it slips threads.
199- // If we don't do this, and a method that uses "await foo;" is invoked through funceval,
200- // we could end up hooking up a callback to push forward the async method's state machine,
201- // the debugger would then abort the funceval after it takes too long, and then continuing
202- // execution could result in another callback being hooked up. At that point we have
203- // multiple callbacks registered to push the state machine, which could result in bad behavior.
204- Debugger . NotifyOfCrossThreadDependency ( ) ;
205-
206- // At this point, taskField should really be null, in which case we want to create the box.
207- // However, in a variety of debugger-related (erroneous) situations, it might be non-null,
208- // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-initialized
209- // as a Task<TResult> rather than as an AsyncStateMachineBox. The worst that happens in such
210- // cases is we lose the ability to properly step in the debugger, as the debugger uses that
211- // object's identity to track this specific builder/state machine. As such, we proceed to
212- // overwrite whatever's there anyway, even if it's non-null.
198+ else
199+ {
200+ // Alert a listening debugger that we can't make forward progress unless it slips threads.
201+ // If we don't do this, and a method that uses "await foo;" is invoked through funceval,
202+ // we could end up hooking up a callback to push forward the async method's state machine,
203+ // the debugger would then abort the funceval after it takes too long, and then continuing
204+ // execution could result in another callback being hooked up. At that point we have
205+ // multiple callbacks registered to push the state machine, which could result in bad behavior.
206+ Debugger . NotifyOfCrossThreadDependency ( ) ;
207+
208+ // At this point, taskField should really be null, in which case we want to create the box.
209+ // However, in a variety of debugger-related (erroneous) situations, it might be non-null,
210+ // e.g. if the Task property is examined in a Watch window, forcing it to be lazily-initialized
211+ // as a Task<TResult> rather than as an AsyncStateMachineBox. The worst that happens in such
212+ // cases is we lose the ability to properly step in the debugger, as the debugger uses that
213+ // object's identity to track this specific builder/state machine. As such, we proceed to
214+ // overwrite whatever's there anyway, even if it's non-null.
213215#if NATIVEAOT
214- // DebugFinalizableAsyncStateMachineBox looks like a small type, but it actually is not because
215- // it will have a copy of all the slots from its parent. It will add another hundred(s) bytes
216- // per each async method in NativeAOT binaries without adding much value. Avoid
217- // generating this extra code until a better solution is implemented.
218- var box = new AsyncStateMachineBox < TStateMachine > ( ) ;
216+ // DebugFinalizableAsyncStateMachineBox looks like a small type, but it actually is not because
217+ // it will have a copy of all the slots from its parent. It will add another hundred(s) bytes
218+ // per each async method in NativeAOT binaries without adding much value. Avoid
219+ // generating this extra code until a better solution is implemented.
220+ var box = new AsyncStateMachineBox < TStateMachine > ( ) ;
219221#else
220- AsyncStateMachineBox < TStateMachine > box = AsyncMethodBuilderCore . TrackAsyncMethodCompletion ?
221- CreateDebugFinalizableAsyncStateMachineBox < TStateMachine > ( ) :
222- new AsyncStateMachineBox < TStateMachine > ( ) ;
222+ AsyncStateMachineBox < TStateMachine > box = AsyncMethodBuilderCore . TrackAsyncMethodCompletion ?
223+ CreateDebugFinalizableAsyncStateMachineBox < TStateMachine > ( ) :
224+ new AsyncStateMachineBox < TStateMachine > ( ) ;
223225#endif
224- taskField = box ; // important: this must be done before storing stateMachine into box.StateMachine!
225- box . StateMachine = stateMachine ;
226- box . Context = currentContext ;
226+ taskField = box ; // important: this must be done before storing stateMachine into box.StateMachine!
227+ box . StateMachine = stateMachine ;
228+ box . Context = currentContext ;
227229
228- // Log the creation of the state machine box object / task for this async method.
229- if ( TplEventSource . Log . IsEnabled ( ) )
230- {
231- TplEventSource . Log . TraceOperationBegin ( box . Id , "Async: " + stateMachine . GetType ( ) . Name , 0 ) ;
232- }
230+ // Log the creation of the state machine box object / task for this async method.
231+ if ( TplEventSource . Log . IsEnabled ( ) )
232+ {
233+ AsyncMethodBuilderCore . LogTraceOperationBegin ( box , stateMachine . GetType ( ) ) ;
234+ }
233235
234- // And if async debugging is enabled, track the task.
235- if ( Threading . Tasks . Task . s_asyncDebuggingEnabled )
236- {
237- Threading . Tasks . Task . AddToActiveTasks ( box ) ;
236+ // And if async debugging is enabled, track the task.
237+ if ( Threading . Tasks . Task . s_asyncDebuggingEnabled )
238+ {
239+ Threading . Tasks . Task . AddToActiveTasks ( box ) ;
240+ }
241+ result = box ;
238242 }
239243
240- return box ;
244+ return result ;
241245 }
242246
243247#if ! NATIVEAOT
0 commit comments