Skip to content

Commit 9016fe2

Browse files
committed
just cut deep stack traces short judiciously
1 parent edab361 commit 9016fe2

File tree

2 files changed

+88
-122
lines changed

2 files changed

+88
-122
lines changed

src/Compiler/Utilities/Cancellable.fs

Lines changed: 82 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ namespace Internal.Utilities.Library.CancellableImplementation
5656

5757
open System
5858
open System.Threading
59-
open FSharp.Compiler
6059

6160
open FSharp.Core.CompilerServices.StateMachineHelpers
6261

@@ -73,7 +72,7 @@ type ITrampolineInvocation =
7372
type CancellableStateMachineData<'T> =
7473

7574
[<DefaultValue(false)>]
76-
val mutable Result: 'T
75+
val mutable Result: Result<'T, exn>
7776

7877
and CancellableStateMachine<'TOverall> = ResumableStateMachine<CancellableStateMachineData<'TOverall>>
7978
and ICancellableStateMachine<'TOverall> = IResumableStateMachine<CancellableStateMachineData<'TOverall>>
@@ -82,39 +81,52 @@ and CancellableResumptionDynamicInfo<'TOverall> = ResumptionDynamicInfo<Cancella
8281
and 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

131143
type ITrampolineInvocation<'T> =
132144
inherit ITrampolineInvocation
133-
abstract Result: 'T
145+
abstract Result: Result<'T, exn>
134146

135147
type IMachineTemplateWrapper<'T> =
136148
abstract Clone: unit -> ITrampolineInvocation<'T>
@@ -157,59 +169,35 @@ type Cancellable<'T>(template: IMachineTemplateWrapper<'T>) =
157169
[<AutoOpen>]
158170
module 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+
201189
type 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

Comments
 (0)