I donβt think there is a direct way to do this using standard library functions from asynchronous F # libraries. The closest us operation is Async.TryCancelled , which calls back when the workflow is canceled, but sending a message from the callback to the code that started the workflow must be done manually.
This is relatively easy to solve using events and the extension from the async F # extensions that I wrote (also included in the FSharpX package) - the GuardedAwaitObservable extension, which can be used to wait for an event to occur (which can be immediately triggered by some operation).
The following Async.StartCancellable method accepts an asynchronous workflow and returns Async<Async<unit>> . When you bind to an external workflow, it starts the argument (for example, Async.StartChild ), and when you bind to the returned internal workflow, it cancels the calculation and waits until it is actually canceled:
open System.Threading module Async = /// Returns an asynchronous workflow 'Async<Async<unit>>'. When called /// using 'let!', it starts the workflow provided as an argument and returns /// a token that can be used to cancel the started work - this is an /// (asynchronously) blocking operation that waits until the workflow /// is actually cancelled let StartCancellable work = async { let cts = new CancellationTokenSource() // Creates an event used for notification let evt = new Event<_>() // Wrap the workflow with TryCancelled and notify when cancelled Async.Start(Async.TryCancelled(work, ignore >> evt.Trigger), cts.Token) // Return a workflow that waits for 'evt' and triggers 'Cancel' // after it attaches the event handler (to avoid missing event occurrence) let waitForCancel = Async.GuardedAwaitObservable evt.Publish cts.Cancel return async.TryFinally(waitForCancel, cts.Dispose) }
EDIT Wrapped the result in TryFinally to get rid of the CancellationTokenSource , as John suggested. I think this should be enough to make sure that it is properly disposed of.
Here is an example that uses this method. The loop function is a simple workflow that I used for testing. The rest of the code runs it, waits 5.5 seconds, and then cancels it:
/// Sample workflow that repeatedly starts and stops long running operation let loop = async { for i in 0 .. 9999 do printfn "Starting: %d" i do! Async.Sleep(1000) printfn "Done: %d" i } // Start the 'loop' workflow, wait for 5.5 seconds and then // cancel it and wait until it finishes current operation async { let! cancelToken = Async.StartCancellable(loop) printfn "started" do! Async.Sleep(5500) printfn "cancelling" do! cancelToken printfn "done" } |> Async.Start
For completeness, the sample with the necessary definitions from FSharpX is here in F # fragments .