1

I have a timeout exception (and I really intend to set a timeout) inside a message loop and I have tried to catch it as follows

let printerAgent = MailboxProcessor.Start(fun inbox-> 
    // the message processing function
    let rec messageLoop() = async{
        try
            // read a message
            let! msg = inbox.Receive 30000
            // process a message
            match msg with
            | Message text ->
                sw.WriteLine("{0}: {1}", DateTime.UtcNow.ToShortTimeString(), text)
                printfn "%s" text
                // loop to top
                return! messageLoop()  
            | Shutdown replyChannel ->
                replyChannel.Reply()
                // We do NOT do return! messageLoop() here
        with 
        | exc -> 
            printfn "%s" exc.Message
        }
    // start the loop 
    messageLoop() 
    )

and I can see the timout message printed in the console, but the program never ends: what am I missing?

This is how I'm calling the printerAgent in my code

printerAgent.PostAndReply( (fun replyChannel -> Shutdown replyChannel), 10000)

Notice that with inbox.Receive() it eventually terminates fine after a few minutes but my objective is setting a timeout (for example of 30 seconds) instead.

  • This might be relevant to you: https://github.com/dotnet/fsharp/issues/10720 – Jim Foye Jan 11 '21 at 14:08
  • @JimFoye Interesting, thanks! Though I see that in that case it is disposed outside the recursive loop so it isn't exactly the same situation... –  Jan 11 '21 at 14:13
  • Welcome to the F# community! It might help you to know that the `MailboxProcessor` has an `Error` event that you could handle. See https://stackoverflow.com/questions/10805035/mailboxprocessor-and-exceptions – Scott Hutchinson Jan 11 '21 at 16:38
  • @ScottHutchinson thanks, well I'm doing `try ... with` because of the `inbox.Receive 30000`. In this moment I've realized that in one of my edits it got deleted and I've restored it in the question. –  Jan 11 '21 at 17:09

1 Answers1

1

I think I see the conceptual problem. I can't terminate the message loop before I receive the final shutdown message (otherwise all the following printerAgent.Post messages sent by the program will simply remain unprocessed in the queue, without blocking the program with any error, and the final shutdown message, sent by a printerAgent.PostAndReply will timeout, also without blocking the program with any error). I should return the message loop so it can actually continue after the timeout exactly as it happens for a normal received message:

with 
| exc -> 
    printfn "%s" exc.Message
    return! messageLoop() // important! I guess I can't really terminate the message loop from here

And at this point the program terminates in the same time: I just see a lot of Timeout of Mailbox.Receive printed in the console (every n=30 seconds while the message loop is idling waiting to receive a message, so they can be informative about the elapsed time).