Skip to content

MailboxProcessor.PostAndAsyncReply doesn't handle CancellationToken properly #4448

@stmax82

Description

@stmax82

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

  0

Tested 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions