@@ -56,7 +56,6 @@ namespace Internal.Utilities.Library.CancellableImplementation
5656
5757open System
5858open System.Threading
59- open FSharp.Compiler
6059
6160open FSharp.Core .CompilerServices .StateMachineHelpers
6261
@@ -73,7 +72,7 @@ type ITrampolineInvocation =
7372type CancellableStateMachineData < 'T > =
7473
7574 [<DefaultValue( false ) >]
76- val mutable Result : 'T
75+ val mutable Result : Result < 'T , exn >
7776
7877and CancellableStateMachine < 'TOverall > = ResumableStateMachine< CancellableStateMachineData< 'TOverall>>
7978and ICancellableStateMachine < 'TOverall > = IResumableStateMachine< CancellableStateMachineData< 'TOverall>>
@@ -82,39 +81,52 @@ and CancellableResumptionDynamicInfo<'TOverall> = ResumptionDynamicInfo<Cancella
8281and CancellableCode < 'TOverall , 'T > = ResumableCode< CancellableStateMachineData< 'TOverall>, 'T>
8382
8483[<Sealed>]
85- type Trampoline () =
86-
87- [<DefaultValue( false ) >]
88- val mutable Token : CancellationToken
89-
90- [<DefaultValue( false ) >]
91- val mutable Exception : ExceptionDispatchInfo voption
84+ type Trampoline ( cancellationToken : CancellationToken ) =
85+ let mutable bindDepth = 0
86+ let mutable storedException : ExceptionDispatchInfo voption = ValueNone
87+ let mutable capturedFramesCount = 0
88+
89+ let captureStackFrame exn =
90+ match storedException with
91+ | ValueSome edi when edi.SourceException = exn ->
92+ try
93+ edi.Throw()
94+ Unchecked.defaultof<_>
95+ with exn ->
96+ capturedFramesCount <- capturedFramesCount + 1
97+ let edi = ExceptionDispatchInfo.Capture exn
98+ storedException <- ValueSome edi
99+ edi.SourceException
100+ | _ ->
101+ capturedFramesCount <- 1
102+ let edi = ExceptionDispatchInfo.Capture exn
103+ storedException <- ValueSome edi
104+ edi.SourceException
92105
93- [<DefaultValue( false ) >]
94- val mutable BindDepth : int
106+ let stack = System.Collections.Generic.Stack< ITrampolineInvocation>()
95107
96108 static let current = new ThreadLocal< Trampoline>()
97109
98- let stack = System.Collections.Generic.Stack< ITrampolineInvocation>()
99-
100- static member IsCancelled = current.Value.Token.IsCancellationRequested
101- static member HasError = current.Value.Exception.IsSome
110+ member this.IsCancelled = cancellationToken.IsCancellationRequested
102111
103- static member Good =
104- not ( current.Value.Token.IsCancellationRequested || current.Value.Exception.IsSome )
112+ member this.ThrowIfCancellationRequested () =
113+ cancellationToken.ThrowIfCancellationRequested ( )
105114
106- static member ThrowIfCancellationRequested () =
107- current.Value.Token.ThrowIfCancellationRequested()
115+ member this.ShoudBounce = bindDepth % 100 = 0
108116
109- static member ShoudBounce = current.Value.BindDepth % 100 = 0
117+ member this.CaptureStackFrame ( exn ) =
118+ if not this.IsCancelled && ( bindDepth < 100 || capturedFramesCount < 200 ) then
119+ captureStackFrame exn
120+ else
121+ exn
110122
111- static member Install () = current.Value <- Trampoline()
123+ static member Install ct = current.Value <- Trampoline ct
112124
113125 member _.Set ( invocation : ITrampolineInvocation ) = stack.Push( invocation)
114126
115127 [<DebuggerHidden>]
116128 member this.Execute ( invocation ) =
117- this.BindDepth <- this.BindDepth + 1
129+ bindDepth <- bindDepth + 1
118130
119131 stack.Push invocation
120132
@@ -124,13 +136,13 @@ type Trampoline() =
124136 if stack.Peek() .IsCompleted then
125137 stack.Pop() |> ignore
126138
127- this.BindDepth <- this.BindDepth - 1
139+ bindDepth <- bindDepth - 1
128140
129141 static member Current = current.Value
130142
131143type ITrampolineInvocation < 'T > =
132144 inherit ITrampolineInvocation
133- abstract Result: 'T
145+ abstract Result: Result < 'T , exn >
134146
135147type IMachineTemplateWrapper < 'T > =
136148 abstract Clone: unit -> ITrampolineInvocation < 'T >
@@ -157,59 +169,35 @@ type Cancellable<'T>(template: IMachineTemplateWrapper<'T>) =
157169[<AutoOpen>]
158170module CancellableCode =
159171
160- let inline captureExn ( exn : exn ) =
161- match exn with
162- | :? OperationCanceledException as oce when oce.CancellationToken = Trampoline.Current.Token -> ()
163- | exn -> Trampoline.Current.Exception <- ValueSome( ExceptionDispatchInfo.Capture exn)
164-
165- Unchecked.defaultof<_>
166-
167- let inline captureStackFrame () =
168- try
169- Trampoline.Current.Exception |> ValueOption.iter _. Throw()
170- with exn ->
171- Trampoline.Current.Exception <- ValueSome <| ExceptionDispatchInfo.Capture exn
172-
173- let inline protect ( code : CancellableCode < _ , _ >) =
172+ let inline filterCancellation ( catch : exn -> CancellableCode < _ , _ >) exn =
174173 CancellableCode( fun sm ->
175174 try
176- code.Invoke(& sm)
177- with exn ->
178- captureExn exn
175+ ( catch exn) .Invoke(& sm)
176+ with :? OperationCanceledException when Trampoline.Current.IsCancelled ->
179177 true )
180178
181- let inline notWhenCancelled ( code : CancellableCode < _ , _ >) =
182- CancellableCode( fun sm -> Trampoline.IsCancelled || ( protect code) .Invoke(& sm))
183-
184- let inline notWhenError ( code : CancellableCode < _ , _ >) =
185- CancellableCode( fun sm -> Trampoline.HasError || ( protect code) .Invoke(& sm))
186-
187- let inline whenGood ( code : CancellableCode < _ , _ >) =
188- CancellableCode( fun sm -> Trampoline.HasError || Trampoline.IsCancelled || ( protect code) .Invoke(& sm))
189-
190- let inline whenGoodApply ( code : _ -> CancellableCode < _ , _ >) arg =
191- CancellableCode( fun sm ->
192- Trampoline.HasError
193- || Trampoline.IsCancelled
194- || ( code arg |> protect) .Invoke(& sm))
195-
196179 let inline throwIfCancellationRequested ( code : CancellableCode < _ , _ >) =
197180 CancellableCode( fun sm ->
198- Trampoline.Current.Token. ThrowIfCancellationRequested()
181+ Trampoline.Current.ThrowIfCancellationRequested()
199182 code.Invoke(& sm))
200183
184+ let inline getResult ( invocation : ITrampolineInvocation < _ >) =
185+ match invocation.Result with
186+ | Ok value -> value
187+ | Error exn -> raise exn
188+
201189type CancellableBuilder () =
202190
203191 member inline _.Delay ( generator : unit -> CancellableCode < 'TOverall , 'T >) : CancellableCode < 'TOverall , 'T > =
204- ResumableCode.Delay( generator) |> protect
192+ ResumableCode.Delay( fun () -> generator ( ) |> throwIfCancellationRequested )
205193
206194 /// Used to represent no-ops like the implicit empty "else" branch of an "if" expression.
207195 [<DefaultValue>]
208196 member inline _.Zero () : CancellableCode < 'TOverall , unit > = ResumableCode.Zero()
209197
210198 member inline _.Return ( value : 'T ) : CancellableCode < 'T , 'T > =
211199 CancellableCode< 'T, _>( fun sm ->
212- sm.Data.Result <- value
200+ sm.Data.Result <- Ok value
213201 true )
214202
215203 /// Chains together a step with its following step.
@@ -218,29 +206,20 @@ type CancellableBuilder() =
218206 member inline _.Combine
219207 ( code1 : CancellableCode < 'TOverall , unit >, code2 : CancellableCode < 'TOverall , 'T >)
220208 : CancellableCode < 'TOverall , 'T > =
221- ResumableCode.Combine( notWhenCancelled code1, whenGood code2) |> protect
209+ ResumableCode.Combine( code1, code2)
222210
223211 /// Builds a step that executes the body while the condition predicate is true.
224212 member inline _.While
225213 ( [<InlineIfLambda>] condition : unit -> bool , body : CancellableCode < 'TOverall , unit >)
226214 : CancellableCode < 'TOverall , unit > =
227- ResumableCode.While( condition, throwIfCancellationRequested body) |> protect
215+ ResumableCode.While( condition, throwIfCancellationRequested body)
228216
229217 /// Wraps a step in a try/with. This catches exceptions both in the evaluation of the function
230218 /// to retrieve the step, and in the continuation of the step (if any).
231- member inline _.TryWith ( body : CancellableCode < 'TOverall , 'T >, catch : exn -> CancellableCode < 'TOverall , 'T >) =
232- CancellableCode< 'TOverall, 'T>( fun sm ->
233- let mutable __stack_fin = true
234- let __stack_body_fin = ( protect body) .Invoke(& sm)
235- __ stack_ fin <- __ stack_ body_ fin
236-
237- if __ stack_ fin && Trampoline.HasError then
238- let __stack_filtered_exn = Trampoline.Current.Exception.Value.SourceException
239- // Clear for now, will get restored if not handled.
240- Trampoline.Current.Exception <- ValueNone
241- __ stack_ fin <- ( catch __ stack_ filtered_ exn |> protect |> notWhenCancelled) .Invoke(& sm)
242-
243- __ stack_ fin)
219+ member inline _.TryWith
220+ ( body : CancellableCode < 'TOverall , 'T >, catch : exn -> CancellableCode < 'TOverall , 'T >)
221+ : CancellableCode < 'TOverall , 'T > =
222+ ResumableCode.TryWith( body, filterCancellation catch)
244223
245224 /// Wraps a step in a try/finally. This catches exceptions both in the evaluation of the function
246225 /// to retrieve the step, and in the continuation of the step (if any).
@@ -257,11 +236,10 @@ type CancellableBuilder() =
257236 member inline _.Using < 'Resource , 'TOverall , 'T when 'Resource :> IDisposable | null >
258237 ( resource : 'Resource , body : 'Resource -> CancellableCode < 'TOverall , 'T >)
259238 : CancellableCode < 'TOverall , 'T > =
260- ResumableCode.Using( resource, whenGoodApply body) |> protect
239+ ResumableCode.Using( resource, body)
261240
262241 member inline _.For ( sequence : seq < 'T >, body : 'T -> CancellableCode < 'TOverall , unit >) : CancellableCode < 'TOverall , unit > =
263242 ResumableCode.For( sequence, fun x -> body x |> throwIfCancellationRequested)
264- |> protect
265243
266244 member inline this.Yield ( value ) = this.Return( value)
267245
@@ -272,32 +250,32 @@ type CancellableBuilder() =
272250 if __ useResumableCode then
273251 let mutable invocation = code.GetInvocation()
274252
275- if Trampoline.ShoudBounce then
253+ if Trampoline.Current. ShoudBounce then
276254 match __ resumableEntry () with
277255 | Some contID ->
278256 sm.ResumptionPoint <- contID
279257 Trampoline.Current.Set invocation
280258 false
281- | None -> ( invocation.Result |> continuation |> whenGood ) .Invoke(& sm)
259+ | None -> ( invocation |> getResult |> continuation ) .Invoke(& sm)
282260 else
283261 Trampoline.Current.Execute invocation
284- ( invocation.Result |> continuation |> whenGood ) .Invoke(& sm)
262+ ( invocation |> getResult |> continuation ) .Invoke(& sm)
285263
286264 else
287265 // Dynamic Bind.
288266
289267 let mutable invocation = code.GetInvocation()
290268
291- if Trampoline.ShoudBounce then
269+ if Trampoline.Current. ShoudBounce then
292270 let cont =
293- CancellableResumptionFunc< 'Data>( fun sm -> ( whenGoodApply continuation invocation.Result ) .Invoke(& sm))
271+ CancellableResumptionFunc< 'Data>( fun sm -> ( invocation |> getResult |> continuation ) .Invoke(& sm))
294272
295273 Trampoline.Current.Set invocation
296274 sm.ResumptionDynamicInfo.ResumptionFunc <- cont
297275 false
298276 else
299277 Trampoline.Current.Execute invocation
300- ( whenGoodApply continuation invocation.Result ) .Invoke(& sm))
278+ ( invocation |> getResult |> continuation ) .Invoke(& sm))
301279
302280 member inline this.ReturnFrom ( comp : Cancellable < 'T >) : CancellableCode < 'T , 'T > = this.Bind( comp, this.Return)
303281
@@ -307,10 +285,14 @@ type CancellableBuilder() =
307285
308286 ( MoveNextMethodImpl<_>( fun sm ->
309287 __ resumeAt sm.ResumptionPoint
310- let __stack_code_fin = ( protect code) .Invoke(& sm)
311288
312- if __ stack_ code_ fin then
313- captureStackFrame ()
289+ try
290+ let __stack_code_fin = code.Invoke(& sm)
291+
292+ if __ stack_ code_ fin then
293+ sm.ResumptionPoint <- - 1
294+ with exn ->
295+ sm.Data.Result <- Error <| Trampoline.Current.CaptureStackFrame exn
314296 sm.ResumptionPoint <- - 1 ))
315297
316298 ( SetStateMachineMethodImpl<_>( fun _ _ -> ()))
@@ -319,14 +301,16 @@ type CancellableBuilder() =
319301 else
320302 // Dynamic Run.
321303
322- let initialResumptionFunc =
323- CancellableResumptionFunc( fun sm -> ( protect code) .Invoke(& sm))
304+ let initialResumptionFunc = CancellableResumptionFunc( fun sm -> code.Invoke(& sm))
324305
325306 let resumptionInfo =
326307 { new CancellableResumptionDynamicInfo<_>( initialResumptionFunc) with
327308 member info.MoveNext ( sm ) =
328- if info.ResumptionFunc.Invoke(& sm) then
329- captureStackFrame ()
309+ try
310+ if info.ResumptionFunc.Invoke(& sm) then
311+ sm.ResumptionPoint <- - 1
312+ with exn ->
313+ sm.Data.Result <- Error <| Trampoline.Current.CaptureStackFrame exn
330314 sm.ResumptionPoint <- - 1
331315
332316 member _.SetStateMachine ( _ , _ ) = ()
@@ -355,35 +339,28 @@ module Cancellable =
355339 use _ = FSharp.Compiler.Cancellable.UsingToken ct
356340
357341 let invocation = code.GetInvocation()
358- Trampoline.Install()
342+ Trampoline.Install ct
359343 Trampoline.Current.Execute invocation
360344 invocation
361345
362346 let runWithoutCancellation code =
363347 let invocation = run CancellationToken.None code
364348
365- if Trampoline.IsCancelled then
366- raise ( OperationCanceledException Trampoline.Current.Token)
367- elif Trampoline.HasError then
368- Trampoline.Current.Exception.Value.Throw()
369- Unchecked.defaultof<_>
349+ if Trampoline.Current.IsCancelled then
350+ failwith " Unexpected cancellation in Cancellable.runWithoutCancellation"
370351 else
371- invocation.Result
352+ getResult invocation
372353
373- let toAsync ( code : Cancellable < _ >) =
354+ let toAsync code =
374355 async {
375356 let! ct = Async.CancellationToken
376357
377358 return !
378359 Async.FromContinuations( fun ( cont , econt , ccont ) ->
379- let invocation = run ct code
380-
381- if Trampoline.IsCancelled then
382- ccont ( OperationCanceledException Trampoline.Current.Token)
383- elif Trampoline.HasError then
384- econt Trampoline.Current.Exception.Value.SourceException
385- else
386- cont invocation.Result)
360+ match run ct code |> _. Result with
361+ | _ when Trampoline.Current.IsCancelled -> ccont ( OperationCanceledException ct)
362+ | Ok value -> cont value
363+ | Error exn -> econt exn)
387364 }
388365
389366 let token () =
0 commit comments