@@ -157,6 +157,8 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
157
157
{
158
158
ExecutionContext ? currentContext = ExecutionContext . Capture ( ) ;
159
159
160
+ IAsyncStateMachineBox result ;
161
+
160
162
// Check first for the most common case: not the first yield in an async method.
161
163
// In this case, the first yield will have already "boxed" the state machine in
162
164
// a strongly-typed manner into an AsyncStateMachineBox. It will already contain
@@ -168,9 +170,8 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
168
170
{
169
171
stronglyTypedBox . Context = currentContext ;
170
172
}
171
- return stronglyTypedBox ;
173
+ result = stronglyTypedBox ;
172
174
}
173
-
174
175
// The least common case: we have a weakly-typed boxed. This results if the debugger
175
176
// or some other use of reflection accesses a property like ObjectIdForDebugger or a
176
177
// method like SetNotificationForWaitCompletion prior to the first await happening. In
@@ -180,7 +181,7 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
180
181
// result in a boxing allocation when storing the TStateMachine if it's a struct, but
181
182
// this only happens in active debugging scenarios where such performance impact doesn't
182
183
// matter.
183
- if ( taskField is AsyncStateMachineBox < IAsyncStateMachine > weaklyTypedBox )
184
+ else if ( taskField is AsyncStateMachineBox < IAsyncStateMachine > weaklyTypedBox )
184
185
{
185
186
// If this is the first await, we won't yet have a state machine, so store it.
186
187
if ( weaklyTypedBox . StateMachine == null )
@@ -192,52 +193,55 @@ private static IAsyncStateMachineBox GetStateMachineBox<TStateMachine>(
192
193
// Update the context. This only happens with a debugger, so no need to spend
193
194
// extra IL checking for equality before doing the assignment.
194
195
weaklyTypedBox . Context = currentContext ;
195
- return weaklyTypedBox ;
196
+ result = weaklyTypedBox ;
196
197
}
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.
213
215
#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 > ( ) ;
219
221
#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 > ( ) ;
223
225
#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 ;
227
229
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
+ }
233
235
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 ;
238
242
}
239
243
240
- return box ;
244
+ return result ;
241
245
}
242
246
243
247
#if ! NATIVEAOT
0 commit comments