@@ -56,9 +56,12 @@ module TplPrimitives =
56
56
57
57
let forceThrowEx = " |PlyForceThrowEx|"
58
58
59
- type [<Struct>] ContinuationStateMachine < 'u > =
60
- #if NETSTANDARD2_ 0
61
- val Builder : AsyncTaskMethodBuilder < 'u >
59
+ type TplResult < 't > = Result< 't, ExceptionDispatchInfo>
60
+
61
+ // https://github.com/dotnet/coreclr/pull/15781/files
62
+ type [<Struct; CompilerGenerated>] ContinuationStateMachine < 'u > =
63
+ #if NETSTANDARD2_ 0
64
+ val Builder : AsyncTaskMethodBuilder < 'u >
62
65
#else
63
66
val Builder : AsyncValueTaskMethodBuilder < 'u >
64
67
#endif
@@ -127,7 +130,7 @@ module TplPrimitives =
127
130
128
131
val private awaiterMethods : 'methods
129
132
val mutable private awaiter : 'awt
130
- val private continuation : 't -> Ply < 'u >
133
+ val private continuation : TplResult < 't > -> Ply < 'u >
131
134
132
135
new ( awaiterMethods, awaiter, continuation) = {
133
136
awaiterMethods = awaiterMethods
@@ -144,9 +147,16 @@ module TplPrimitives =
144
147
145
148
override this.GetNext () =
146
149
Debug.Assert( this.awaiterMethods.IsCompleted & this.awaiter || ( typeof< 'awt> = typeof< YieldAwaitable.YieldAwaiter>), " Forcing an async here" )
147
- this.continuation ( this.awaiterMethods.GetResult & this.awaiter)
148
150
149
- and [<Sealed>] PlyAwaitable < 't , 'u > ( awaitable : Awaitable < 't >, continuation : 't -> Ply < 'u >) =
151
+ let result =
152
+ try
153
+ Ok( this.awaiterMethods.GetResult & this.awaiter)
154
+ with ex ->
155
+ Error( ExceptionDispatchInfo.Capture( ex))
156
+
157
+ this.continuation result
158
+
159
+ and [<Sealed>] PlyAwaitable < 't , 'u > ( awaitable : Awaitable < 't >, continuation : 't -> Ply < 'u >) =
150
160
inherit Awaitable< 'u>()
151
161
let mutable awaitable = awaitable
152
162
@@ -404,8 +414,12 @@ module TplPrimitives =
404
414
// Secondly, for every GetResult — because all calls to bind overloads are wrapped by TaskBuilder.Run — we are
405
415
// already running within our own Excecution context bubble. No need to be careful calling GetResult.
406
416
417
+ // Await exists for binary compatibility.
418
+ static member Await < 'methods , 'awt , 't when 'methods :> IAwaiterMethods < 'awt , 't >>( awt : byref < 'awt >, cont : 't -> Ply < 'u >) =
419
+ Ply( await = TplAwaitable( defaultof< 'methods>, awt, fun r -> match r with Ok t -> cont t | Error e -> e.Throw(); defaultof<_>))
420
+
407
421
// We keep Await non inline to protect internals to maximize binary compatibility.
408
- static member Await < 'methods , 'awt , 't when 'methods :> IAwaiterMethods < 'awt , 't >>( awt : byref < 'awt >, cont : 't -> Ply < 'u >) =
422
+ static member AwaitResult < 'methods , 'awt , 't when 'methods :> IAwaiterMethods < 'awt , 't >>( awt : byref < 'awt >, cont : TplResult < 't > -> Ply < 'u >) =
409
423
Ply( await = TplAwaitable( defaultof< 'methods>, awt, cont))
410
424
411
425
static member inline Specialized < 'methods , ^awt , 't
@@ -418,7 +432,8 @@ module TplPrimitives =
418
432
cont ( ^awt : ( member GetResult : unit -> 't) ( awt))
419
433
else
420
434
let mutable mutAwt = awt
421
- Binder< 'u>. Await< 'methods,_,_>(& mutAwt, ( fun x -> cont x))
435
+ // Having the edi.Throw here means user stack frames will get captured, as this code will get inlined into cont.
436
+ Binder< 'u>. AwaitResult< 'methods,_,_>(& mutAwt, ( fun r -> match r with Ok t -> cont t | Error e -> e.Throw(); defaultof<_>))
422
437
423
438
// We have special treatment for unknown taskLike types where we wrap the continuation in a unit func
424
439
// This allows us to use a single GenericAwaiterMethods type (zero alloc, small drop in perf) instead of an object expression.
0 commit comments