Friday, August 5, 2022

[FIXED] How to use reraise in async workflows in F#?

Issue

I needed to reraise an exception that occurs while executing an async block, after logging the exception.

When I do the following the compiler thinks that I am not calling the reraise function from within the handler. What am I doing wrong?

let executeAsync context = async {
    traceContext.Properties.Add("CorrelationId", context.CorrelationId)
    try
        do! runAsync context
        return None
    with
        | e when isCriticalException(e) ->
            logCriticalException e
            reraise()
        | e ->
            logException e
            return Some(e)
}

Solution

Rough! I think this is impossible, because reraise corresponds to a special IL instruction that grabs the exception from the top of the stack, but the way async expressions are compiled into a chain of continuations, I don't think the semantics hold!

For the same reason, the following won't compile either:

try
    (null:string).ToString()
with e ->
    (fun () -> reraise())()

In these situations, where I need to handle the exception outside of the actual with body, and would like to emulate reraise (that is, preserve the stack trace of the exception), I use this solution, so all together your code would look like:

let inline reraisePreserveStackTrace (e:Exception) =
    let remoteStackTraceString = typeof<exn>.GetField("_remoteStackTraceString", BindingFlags.Instance ||| BindingFlags.NonPublic);
    remoteStackTraceString.SetValue(e, e.StackTrace + Environment.NewLine);
    raise e

let executeAsync context = async {
    traceContext.Properties.Add("CorrelationId", context.CorrelationId)
    try
        do! runAsync context
        return None
    with
        | e when isCriticalException(e) ->
            logCriticalException e
            reraisePreserveStackTrace e
        | e ->
            logException e
            return Some(e)
}

Update: .NET 4.5 introduced ExceptionDispatchInfo which may allow a cleaner implementation of reraisePreserveStackTrace above.



Answered By - Stephen Swensen
Answer Checked By - David Marino (PHPFixing Volunteer)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.