MailboxProcessor and exceptions

f#, mailboxprocessor

Solution

I think the reason why the `MailboxProcessor` in F# does not contain any mechanism for handling exceptions is that it is not clear what is the best way for doing that. For example, you may want to have a global event that is triggered when an unhandled exception happens, but you may want to rethrow the exception on the next call to `Post` or `PostAndReply`.

Both of the options can be implemented based on the standard `MailboxProcessor`, so it is possible to add the behaviour you want. For example, the following snippet shows `HandlingMailbox` that adds a global exception handler. It has the same interface as normal `MailboxProcessor` (I omitted some methods), but it adds `OnError` event that is triggered when an exception happens:

type HandlingMailbox<'T> private(f:HandlingMailbox<'T> -> Async<unit>) as self =
  let event = Event<_>()
  let inbox = new MailboxProcessor<_>(fun inbox -> async {
    try 
      return! f self
    with e ->
      event.Trigger(e) })
  member x.OnError = event.Publish
  member x.Start() = inbox.Start()
  member x.Receive() = inbox.Receive()
  member x.Post(v:'T) = inbox.Post(v)
  static member Start(f) =
    let mbox = new HandlingMailbox<_>(f)
    mbox.Start()
    mbox

To use it, you would write the same code as what you wrote before, but you can now handle exceptions asynchronously:

let counter = HandlingMailbox<_>.Start(fun inbox -> async {
  while true do 
    printfn "waiting for data..." 
    let! data = inbox.Receive() 
    failwith "fail" })

counter.OnError.Add(printfn "Exception: %A")
counter.Post(42) 

Problem

I wonder, why `MailboxProcessor`'s default strategy of handling exceptions is just silently ignore them. For example: ``` let counter = MailboxProcessor.Start(fun inbox -> let rec loop() = async { printfn "waiting for data..." let! data = inbox.Receive() failwith "fail" // simulate throwing of an exception printfn "Got: %d" data return! loop() } loop ()) () counter.Post(42) counter.Post(43) counter.Post(44) Async.Sleep 1000 |> Async.RunSynchronously ``` and nothing happens. There is no fatal stop of the program execution, or message box with "An unhandled exception" arises. Nothing. This situation becomes worse if someone uses `PostAndReply` method: a guaranteed deadlock as the result. Any reasons for such behavior?

Original source