Reputation: 37131
module Async =
let forever = Async.FromContinuations ignore
let withTimeout timeout action =
async {
let! child = Async.StartChild (action, timeout)
return! child
}
async {
printfn "Started... "
do!
Async.forever
|> Async.withTimeout 1_000
printfn "Finished. "
}
|> Async.RunSynchronously
Why does this code not terminate? I would expect it to finish after 1000ms.
$ dotnet --version
5.0.103
Upvotes: 2
Views: 88
Reputation: 243096
In F# async, checking for cancellation is collaborative - this means that the individual primitive asynchronous operations have to "collaborate" with the system and check for cancellation (if they do something that does not automatically propagate the cancellation token).
In other words, in F# async, cancellation checks are done automatically on do!
and let!
etc., but if you block or never call a continuation, then the checks do not happen.
You could modify your forever
to register a cancellation handler and trigger the operation cancelled continuation:
let forever : Async<unit> = async {
let! tok = Async.CancellationToken
return! Async.FromContinuations(fun (cont, econt, ccont) ->
tok.Register(fun _ -> ccont(System.OperationCanceledException("cancelled!")))
|> ignore
)
}
With this, running your sample prints "Starting" and then it fails with TimeoutException
. If you actually wanted to print the "finish" text, you could write:
async {
printfn "Started... "
try
do! Async.forever |> Async.withTimeout 1_000
finally
printfn "Finished. " }
|> Async.RunSynchronously
Upvotes: 4