F# how to stop Async.Start

Viewed 183

Hi I have a question about async in F#.

So I have a simple procedure that runs in background that is placed in a member of a type and it looks like:

type Sender() = 
     member this.Start(udpConectionPool) = async {
        (* Some operation that continuously sends something over udp*)
     } |> Async.Start

So this starts and begins to continuously sends frames over UDP without blocking rest of the program, but from time to time i want to restart thread (let us say i want to add new endpoint it would send it to that is udpConnectionPool parameter).

I was thinking about something like dumping task to member and then:

member this.Stop() = async {
    do! (*stop async start member that contains task*)
}

And then I can restart this task with updated connection pool, but I don't know if I can do that.

My question is, Is it possible to stop such task, or if not is there a better way to do it?

1 Answers

The standard way of cancelling F# async workflows is using a CancellationToken. When you call Async.Start, you can provide a cancellation token. When the token gets cancelled, the async workflow will stop (after the current blocking work finishes):

open System.Threading

let cts = new CancellationTokenSource()
let work = async { (* ... *) }
Async.Start(work, cts.Token)

cts.Cancel() // Sometime later from another thread

To integrate this with the Sender, you could either store the current CancellationTokenSource and have a Stop method that cancels it (if you want to keep this inside a stateful class). Alternatively, you could return IDisposable from the Start method in a way that is similar to how the Observable interface work:

type Sender () = 
  member this.Start(udpConnectionPool) = 
    let cts = new CancellationTokenSource()
    let work = async { (* ... *) }
    Async.Start(work, cts.Token)
    { new System.IDisposable with
      member x.Dispose() = cts.Cancel() }

This way, the caller of Start is responsible for storing the returned IDisposable and disposing of it before calling Start again.

Related