-
Notifications
You must be signed in to change notification settings - Fork 830
Description
MailboxProcessor.PostAndAsyncReply can get stuck forever when the parent gets cancelled.
The following starts two asyncs in parallel and then cancels their parent.
One of the asyncs exits by printing "good async exited", while the other one gets stuck in PostAndAsyncReply forever (it never prints "bad async exited").
open System
open System.Threading
type Message = Add of int * int * AsyncReplyChannel<int>
[<EntryPoint>]
let main argv =
use cancel = new CancellationTokenSource(3000)
let goodAsync = async {
try
for i in Seq.initInfinite (fun i -> i) do
if i % 10000000 = 0 then
printfn "good async working..."
finally
printfn "good async exited"
}
let badAsync (mbox:MailboxProcessor<Message>) = async {
try
printfn "bad async working..."
let! result = mbox.PostAndAsyncReply (fun reply -> Add (1, 2, reply)) // <- stuck in here forever :(
printfn "%d" result
finally
printfn "bad async exited" // <- we never get here
}
let mbox = MailboxProcessor.Start(fun inbox -> async {
let! Add (a, b, reply) = inbox.Receive()
do! Async.Sleep 1000000
reply.Reply (a+b)
}, cancel.Token)
[goodAsync; badAsync mbox]
|> Async.Parallel
|> Async.Ignore
|> fun x -> Async.Start(x, cancel.Token)
Console.ReadLine() |> ignore
0Tested with VS 2017 15.5.7 and net461 and netcoreapp2.0.. though the behaviour is the same since at least 2014.
With the MailboxProcessor and PostAndAsyncReply being such basic functionality, I think this should be cancelable. Tomas Petricek once wrote:
In F# asynchronous workflows, the CancellationToken object is passed around automatically under the cover. This means that we don't have to do anything special to support cancellation. When running asynchronous workflow, we can give it cancellation token and everything will work automatically.