Skip to content

Commit a8e059f

Browse files
dsymeKevinRansom
authored andcommitted
Improve async stack traces (#4867)
* very early prototype * async stack traces * async cleanup * minor fix * async cleanup * more async cleanup * integrate async-cleanup * async cleanup * fix build * more cleanup * minor fixes * minor fixes * full exception stacktraces * fix test * fix test * code review * cleanup naming * fix build * undo rethrow and integrate cleanup * apply renamings * Further cleanup in control.fs * add tests and add filtering TryWith, plus other cleanup * integrate cleanup * fix tests * test only runs on .net framework * slightly tweak primitives to be more suitable for later optimization * slightly tweak primitives to be more suitable for later optimization * update baselines * add check that no line 0 appear in stack * update baseline * use struct wrapper for async activation * simplify code * simplify code * update baselines * update baselines * fix baseline * remove dead code * simplify code * apply DebuggerHidden in a couple more places
1 parent e5323b5 commit a8e059f

File tree

15 files changed

+1501
-529
lines changed

15 files changed

+1501
-529
lines changed

src/fsharp/FSharp.Core/async.fs

Lines changed: 390 additions & 283 deletions
Large diffs are not rendered by default.

src/fsharp/FSharp.Core/async.fsi

Lines changed: 117 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ namespace Microsoft.FSharp.Control
2525
/// computation expressions can check the cancellation condition regularly. Synchronous
2626
/// computations within an asynchronous computation do not automatically check this condition.</remarks>
2727
28-
[<Sealed>]
29-
[<NoEquality; NoComparison>]
30-
[<CompiledName("FSharpAsync`1")>]
28+
[<Sealed; NoEquality; NoComparison; CompiledName("FSharpAsync`1")>]
3129
type Async<'T>
3230

3331
/// <summary>This static class holds members for creating and manipulating asynchronous computations.</summary>
@@ -417,7 +415,6 @@ namespace Microsoft.FSharp.Control
417415
static member StartImmediate:
418416
computation:Async<unit> * ?cancellationToken:CancellationToken-> unit
419417

420-
421418
/// <summary>Runs an asynchronous computation, starting immediately on the current operating system,
422419
/// but also returns the execution as <c>System.Threading.Tasks.Task</c>
423420
/// </summary>
@@ -435,6 +432,112 @@ namespace Microsoft.FSharp.Control
435432
computation:Async<'T> * ?cancellationToken:CancellationToken-> Task<'T>
436433

437434

435+
/// <summary>The F# compiler emits references to this type to implement F# async expressions.</summary>
436+
type AsyncReturn
437+
438+
/// <summary>The F# compiler emits references to this type to implement F# async expressions.</summary>
439+
[<Struct; NoEquality; NoComparison>]
440+
type AsyncActivation<'T> =
441+
442+
/// <summary>The F# compiler emits calls to this function to implement F# async expressions.</summary>
443+
///
444+
/// <returns>A value indicating asynchronous execution.</returns>
445+
member IsCancellationRequested: bool
446+
447+
/// <summary>The F# compiler emits calls to this function to implement F# async expressions.</summary>
448+
///
449+
/// <returns>A value indicating asynchronous execution.</returns>
450+
member OnSuccess: 'T -> AsyncReturn
451+
452+
/// <summary>The F# compiler emits calls to this function to implement F# async expressions.</summary>
453+
member OnExceptionRaised: unit -> unit
454+
455+
/// <summary>The F# compiler emits calls to this function to implement F# async expressions.</summary>
456+
///
457+
/// <returns>A value indicating asynchronous execution.</returns>
458+
member OnCancellation: unit -> AsyncReturn
459+
460+
/// Used by MailboxProcessor
461+
member internal QueueContinuationWithTrampoline: 'T -> AsyncReturn
462+
/// Used by MailboxProcessor
463+
member internal CallContinuation: 'T -> AsyncReturn
464+
465+
[<NoEquality; NoComparison>]
466+
// Internals used by MailboxProcessor
467+
type internal AsyncResult<'T> =
468+
| Ok of 'T
469+
| Error of ExceptionDispatchInfo
470+
| Canceled of OperationCanceledException
471+
472+
[<Sealed>]
473+
/// <summary>Entry points for generated code</summary>
474+
module AsyncPrimitives =
475+
476+
/// <summary>The F# compiler emits calls to this function to implement F# async expressions.</summary>
477+
///
478+
/// <param name="body">The body of the async computation.</param>
479+
///
480+
/// <returns>The async computation.</returns>
481+
val MakeAsync: body:(AsyncActivation<'T> -> AsyncReturn) -> Async<'T>
482+
483+
/// <summary>The F# compiler emits calls to this function to implement constructs for F# async expressions.</summary>
484+
///
485+
/// <param name="computation">The async computation.</param>
486+
/// <param name="ctxt">The async activation.</param>
487+
///
488+
/// <returns>A value indicating asynchronous execution.</returns>
489+
val Invoke: computation: Async<'T> -> ctxt:AsyncActivation<'T> -> AsyncReturn
490+
491+
/// <summary>The F# compiler emits calls to this function to implement constructs for F# async expressions.</summary>
492+
///
493+
/// <param name="ctxt">The async activation.</param>
494+
/// <param name="result">The result of the first part of the computation.</param>
495+
/// <param name="part2">A function returning the second part of the computation.</param>
496+
///
497+
/// <returns>A value indicating asynchronous execution.</returns>
498+
val CallThenInvoke: ctxt:AsyncActivation<'T> -> result1:'U -> part2:('U -> Async<'T>) -> AsyncReturn
499+
500+
/// <summary>The F# compiler emits calls to this function to implement the <c>let!</c> construct for F# async expressions.</summary>
501+
///
502+
/// <param name="ctxt">The async activation.</param>
503+
/// <param name="part2">A function returning the second part of the computation.</param>
504+
///
505+
/// <returns>An async activation suitable for running part1 of the asynchronous execution.</returns>
506+
val Bind: ctxt:AsyncActivation<'T> -> part1:Async<'U> -> part2:('U -> Async<'T>) -> AsyncReturn
507+
508+
/// <summary>The F# compiler emits calls to this function to implement the <c>try/finally</c> construct for F# async expressions.</summary>
509+
///
510+
/// <param name="ctxt">The async activation.</param>
511+
/// <param name="computation">The computation to protect.</param>
512+
/// <param name="finallyFunction">The finally code.</param>
513+
///
514+
/// <returns>A value indicating asynchronous execution.</returns>
515+
val TryFinally: ctxt:AsyncActivation<'T> -> computation: Async<'T> -> finallyFunction: (unit -> unit) -> AsyncReturn
516+
517+
/// <summary>The F# compiler emits calls to this function to implement the <c>try/with</c> construct for F# async expressions.</summary>
518+
///
519+
/// <param name="ctxt">The async activation.</param>
520+
/// <param name="computation">The computation to protect.</param>
521+
/// <param name="catchFunction">The exception filter.</param>
522+
///
523+
/// <returns>A value indicating asynchronous execution.</returns>
524+
val TryWith: ctxt:AsyncActivation<'T> -> computation: Async<'T> -> catchFunction: (Exception -> Async<'T> option) -> AsyncReturn
525+
526+
[<Sealed; AutoSerializable(false)>]
527+
// Internals used by MailboxProcessor
528+
type internal ResultCell<'T> =
529+
new : unit -> ResultCell<'T>
530+
member GetWaitHandle: unit -> WaitHandle
531+
member Close: unit -> unit
532+
interface IDisposable
533+
member RegisterResult: 'T * reuseThread: bool -> AsyncReturn
534+
member GrabResult: unit -> 'T
535+
member ResultAvailable : bool
536+
member AwaitResult_NoDirectCancelOrTimeout : Async<'T>
537+
member TryWaitForResultSynchronously: ?timeout: int -> 'T option
538+
539+
// Internals used by MailboxProcessor
540+
val internal CreateAsyncResultAsync : AsyncResult<'T> -> Async<'T>
438541

439542
[<CompiledName("FSharpAsyncBuilder")>]
440543
[<Sealed>]
@@ -473,7 +576,7 @@ namespace Microsoft.FSharp.Control
473576
/// <param name="computation1">The first part of the sequenced computation.</param>
474577
/// <param name="computation2">The second part of the sequenced computation.</param>
475578
/// <returns>An asynchronous computation that runs both of the computations sequentially.</returns>
476-
member Combine : computation1:Async<unit> * computation2:Async<'T> -> Async<'T>
579+
member inline Combine : computation1:Async<unit> * computation2:Async<'T> -> Async<'T>
477580

478581
/// <summary>Creates an asynchronous computation that runs <c>computation</c> repeatedly
479582
/// until <c>guard()</c> becomes false.</summary>
@@ -496,15 +599,15 @@ namespace Microsoft.FSharp.Control
496599
/// <c>async { ... }</c> computation expression syntax.</remarks>
497600
/// <param name="value">The value to return from the computation.</param>
498601
/// <returns>An asynchronous computation that returns <c>value</c> when executed.</returns>
499-
member Return : value:'T -> Async<'T>
602+
member inline Return : value:'T -> Async<'T>
500603

501604
/// <summary>Delegates to the input computation.</summary>
502605
///
503606
/// <remarks>The existence of this method permits the use of <c>return!</c> in the
504607
/// <c>async { ... }</c> computation expression syntax.</remarks>
505608
/// <param name="computation">The input computation.</param>
506609
/// <returns>The input computation.</returns>
507-
member ReturnFrom : computation:Async<'T> -> Async<'T>
610+
member inline ReturnFrom : computation:Async<'T> -> Async<'T>
508611

509612
/// <summary>Creates an asynchronous computation that runs <c>generator</c>.</summary>
510613
///
@@ -538,7 +641,7 @@ namespace Microsoft.FSharp.Control
538641
/// <param name="binder">The function to bind the result of <c>computation</c>.</param>
539642
/// <returns>An asynchronous computation that performs a monadic bind on the result
540643
/// of <c>computation</c>.</returns>
541-
member Bind: computation: Async<'T> * binder: ('T -> Async<'U>) -> Async<'U>
644+
member inline Bind: computation: Async<'T> * binder: ('T -> Async<'U>) -> Async<'U>
542645
543646
/// <summary>Creates an asynchronous computation that runs <c>computation</c>. The action <c>compensation</c> is executed
544647
/// after <c>computation</c> completes, whether <c>computation</c> exits normally or by an exception. If <c>compensation</c> raises an exception itself
@@ -553,7 +656,7 @@ namespace Microsoft.FSharp.Control
553656
/// exception (including cancellation).</param>
554657
/// <returns>An asynchronous computation that executes computation and compensation afterwards or
555658
/// when an exception is raised.</returns>
556-
member TryFinally : computation:Async<'T> * compensation:(unit -> unit) -> Async<'T>
659+
member inline TryFinally : computation:Async<'T> * compensation:(unit -> unit) -> Async<'T>
557660

558661
/// <summary>Creates an asynchronous computation that runs <c>computation</c> and returns its result.
559662
/// If an exception happens then <c>catchHandler(exn)</c> is called and the resulting computation executed instead.</summary>
@@ -562,11 +665,14 @@ namespace Microsoft.FSharp.Control
562665
///
563666
/// The existence of this method permits the use of <c>try/with</c> in the
564667
/// <c>async { ... }</c> computation expression syntax.</remarks>
668+
///
565669
/// <param name="computation">The input computation.</param>
566670
/// <param name="catchHandler">The function to run when <c>computation</c> throws an exception.</param>
567671
/// <returns>An asynchronous computation that executes <c>computation</c> and calls <c>catchHandler</c> if an
568672
/// exception is thrown.</returns>
569-
member TryWith : computation:Async<'T> * catchHandler:(exn -> Async<'T>) -> Async<'T>
673+
member inline TryWith : computation:Async<'T> * catchHandler:(exn -> Async<'T>) -> Async<'T>
674+
675+
// member inline TryWithFilter : computation:Async<'T> * catchHandler:(exn -> Async<'T> option) -> Async<'T>
570676

571677
/// Generate an object used to build asynchronous computations using F# computation expressions. The value
572678
/// 'async' is a pre-defined instance of this type.
@@ -659,41 +765,6 @@ namespace Microsoft.FSharp.Control
659765
#endif
660766

661767
// Internals used by MailboxProcessor
662-
module internal AsyncImpl =
768+
module internal AsyncBuilderImpl =
663769
val async : AsyncBuilder
664770

665-
[<Sealed>]
666-
// Internals used by MailboxProcessor
667-
type internal AsyncReturn
668-
669-
[<Sealed>]
670-
// Internals used by MailboxProcessor
671-
type internal AsyncActivation<'T> =
672-
member QueueContinuationWithTrampoline: 'T -> AsyncReturn
673-
member CallContinuation: 'T -> AsyncReturn
674-
675-
[<NoEquality; NoComparison>]
676-
// Internals used by MailboxProcessor
677-
type internal AsyncResult<'T> =
678-
| Ok of 'T
679-
| Error of ExceptionDispatchInfo
680-
| Canceled of OperationCanceledException
681-
682-
// Internals used by MailboxProcessor
683-
module internal AsyncPrimitives =
684-
685-
[<Sealed; AutoSerializable(false)>]
686-
type internal ResultCell<'T> =
687-
new : unit -> ResultCell<'T>
688-
member GetWaitHandle: unit -> WaitHandle
689-
member Close: unit -> unit
690-
interface IDisposable
691-
member RegisterResult: 'T * reuseThread: bool -> AsyncReturn
692-
member GrabResult: unit -> 'T
693-
member ResultAvailable : bool
694-
member AwaitResult_NoDirectCancelOrTimeout : Async<'T>
695-
member TryWaitForResultSynchronously: ?timeout: int -> 'T option
696-
697-
val CreateAsyncResultAsync : AsyncResult<'T> -> Async<'T>
698-
699-
val MakeAsync : (AsyncActivation<'T> -> AsyncReturn) -> Async<'T>

src/fsharp/FSharp.Core/mailbox.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Microsoft.FSharp.Control
77
open Microsoft.FSharp.Core
88
open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators
99
open Microsoft.FSharp.Control
10-
open Microsoft.FSharp.Control.AsyncImpl
10+
open Microsoft.FSharp.Control.AsyncBuilderImpl
1111
open Microsoft.FSharp.Control.AsyncPrimitives
1212
open Microsoft.FSharp.Collections
1313

src/fsharp/FSharp.Core/prim-types.fs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,17 +2764,17 @@ namespace Microsoft.FSharp.Core
27642764
// Function Values
27652765

27662766
[<AbstractClass>]
2767-
type FSharpTypeFunc() =
2767+
type FSharpTypeFunc [<DebuggerHidden>] () =
27682768
abstract Specialize<'T> : unit -> obj
27692769

27702770
[<AbstractClass>]
2771-
type FSharpFunc<'T,'Res>() =
2771+
type FSharpFunc<'T,'Res> [<DebuggerHidden>] () =
27722772
abstract Invoke : 'T -> 'Res
27732773

27742774
module OptimizedClosures =
27752775

27762776
[<AbstractClass>]
2777-
type FSharpFunc<'T,'U,'V>() =
2777+
type FSharpFunc<'T,'U,'V> [<DebuggerHidden>] () =
27782778
inherit FSharpFunc<'T,('U -> 'V)>()
27792779
abstract Invoke : 'T * 'U -> 'V
27802780
override f.Invoke(t) = (fun u -> f.Invoke(t,u))
@@ -2787,7 +2787,7 @@ namespace Microsoft.FSharp.Core
27872787
member x.Invoke(t,u) = (retype func : FSharpFunc<'T,FSharpFunc<'U,'V>>).Invoke(t).Invoke(u) }
27882788

27892789
[<AbstractClass>]
2790-
type FSharpFunc<'T,'U,'V,'W>() =
2790+
type FSharpFunc<'T,'U,'V,'W> [<DebuggerHidden>] () =
27912791
inherit FSharpFunc<'T,('U -> 'V -> 'W)>()
27922792
abstract Invoke : 'T * 'U * 'V -> 'W
27932793
override f.Invoke(t) = (fun u v -> f.Invoke(t,u,v))
@@ -2805,7 +2805,7 @@ namespace Microsoft.FSharp.Core
28052805
member x.Invoke(t,u,v) = (retype func : FSharpFunc<'T,('U -> 'V -> 'W)>).Invoke(t) u v }
28062806

28072807
[<AbstractClass>]
2808-
type FSharpFunc<'T,'U,'V,'W,'X>() =
2808+
type FSharpFunc<'T,'U,'V,'W,'X> [<DebuggerHidden>] () =
28092809
inherit FSharpFunc<'T,('U -> 'V -> 'W -> 'X)>()
28102810
abstract Invoke : 'T * 'U * 'V * 'W -> 'X
28112811
static member Adapt(func : 'T -> 'U -> 'V -> 'W -> 'X) =
@@ -2828,7 +2828,7 @@ namespace Microsoft.FSharp.Core
28282828
override f.Invoke(t) = (fun u v w -> f.Invoke(t,u,v,w))
28292829

28302830
[<AbstractClass>]
2831-
type FSharpFunc<'T,'U,'V,'W,'X,'Y>() =
2831+
type FSharpFunc<'T,'U,'V,'W,'X,'Y> [<DebuggerHidden>] () =
28322832
inherit FSharpFunc<'T,('U -> 'V -> 'W -> 'X -> 'Y)>()
28332833
abstract Invoke : 'T * 'U * 'V * 'W * 'X -> 'Y
28342834
override f.Invoke(t) = (fun u v w x -> f.Invoke(t,u,v,w,x))

tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,29 @@ Microsoft.FSharp.Collections.SetModule: TState FoldBack[T,TState](Microsoft.FSha
567567
Microsoft.FSharp.Collections.SetModule: TState Fold[T,TState](Microsoft.FSharp.Core.FSharpFunc`2[TState,Microsoft.FSharp.Core.FSharpFunc`2[T,TState]], TState, Microsoft.FSharp.Collections.FSharpSet`1[T])
568568
Microsoft.FSharp.Collections.SetModule: T[] ToArray[T](Microsoft.FSharp.Collections.FSharpSet`1[T])
569569
Microsoft.FSharp.Collections.SetModule: Void Iterate[T](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Collections.FSharpSet`1[T])
570+
Microsoft.FSharp.Control.AsyncActivation`1[T]: Boolean Equals(System.Object)
571+
Microsoft.FSharp.Control.AsyncActivation`1[T]: Boolean IsCancellationRequested
572+
Microsoft.FSharp.Control.AsyncActivation`1[T]: Boolean get_IsCancellationRequested()
573+
Microsoft.FSharp.Control.AsyncActivation`1[T]: Int32 GetHashCode()
574+
Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn OnCancellation()
575+
Microsoft.FSharp.Control.AsyncActivation`1[T]: Void OnExceptionRaised()
576+
Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn OnSuccess(T)
577+
Microsoft.FSharp.Control.AsyncActivation`1[T]: System.String ToString()
578+
Microsoft.FSharp.Control.AsyncActivation`1[T]: System.Type GetType()
579+
Microsoft.FSharp.Control.AsyncPrimitives: Boolean Equals(System.Object)
580+
Microsoft.FSharp.Control.AsyncPrimitives: Int32 GetHashCode()
581+
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Bind[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], Microsoft.FSharp.Control.FSharpAsync`1[TResult], Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]])
582+
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn CallThenInvoke[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], TResult, Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]])
583+
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Invoke[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Control.AsyncActivation`1[T])
584+
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn TryFinally[T](Microsoft.FSharp.Control.AsyncActivation`1[T], Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit])
585+
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn TryWith[T](Microsoft.FSharp.Control.AsyncActivation`1[T], Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Control.FSharpAsync`1[T]]])
586+
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.FSharpAsync`1[T] MakeAsync[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.AsyncActivation`1[T],Microsoft.FSharp.Control.AsyncReturn])
587+
Microsoft.FSharp.Control.AsyncPrimitives: System.String ToString()
588+
Microsoft.FSharp.Control.AsyncPrimitives: System.Type GetType()
589+
Microsoft.FSharp.Control.AsyncReturn: Boolean Equals(System.Object)
590+
Microsoft.FSharp.Control.AsyncReturn: Int32 GetHashCode()
591+
Microsoft.FSharp.Control.AsyncReturn: System.String ToString()
592+
Microsoft.FSharp.Control.AsyncReturn: System.Type GetType()
570593
Microsoft.FSharp.Control.CommonExtensions: Boolean Equals(System.Object)
571594
Microsoft.FSharp.Control.CommonExtensions: Int32 GetHashCode()
572595
Microsoft.FSharp.Control.CommonExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncWrite(System.IO.Stream, Byte[], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])

0 commit comments

Comments
 (0)