Skip to content

Instantly share code, notes, and snippets.

@colinbull
Created April 14, 2013 18:34
Show Gist options
  • Save colinbull/5383720 to your computer and use it in GitHub Desktop.
Save colinbull/5383720 to your computer and use it in GitHub Desktop.
Simple CancellableTask builder
open System
open System.Threading
open System.Threading.Tasks
type CancelToken =
| Token of CancellationToken
| Default
with
static member Create(cts:CancellationTokenSource) =
Token(cts.Token)
type Result<'a> =
| Cancelled
| Success of 'a
| Err of exn
type CancellableTaskBuilder() =
let lift (t:Task<_>) = (fun (token:CancelToken) -> t)
let ret x =
let complete = TaskCompletionSource()
complete.SetResult x
complete.Task
let rec bind (m:CancelToken -> Task<'a>) (comp : 'a -> CancelToken -> Task<'b>) =
(fun token ->
let currentTask = m token
let cToken =
match token with
| Token t -> t
| Default -> Async.DefaultCancellationToken
currentTask.ContinueWith((fun (t:Task<_>) -> (comp t.Result token)), cToken).Unwrap())
member this.Return x = lift (ret x)
member this.ReturnFrom m = m
member this.Bind(m, comp) = bind m comp
member this.Bind(t, comp) = bind (lift t) comp
member this.Zero() = lift (ret ())
member this.Delay f = bind (lift (ret ())) f
member this.Start(f : CancelToken -> Task<'a>, token) =
try
let comp = f token
Success(comp.Result)
with
| :? AggregateException as e ->
match e.InnerException with
| :? TaskCanceledException as tc -> Result.Cancelled
| _ -> Result.Err (e.Flatten())
| :? OperationCanceledException as e -> Result.Cancelled
| _ as e -> Result.Err e
member this.ToAsync(f : CancelToken -> Task<'a>, token) =
async {
try
let comp = f token
return Success(comp.Result)
with
| :? AggregateException as e ->
match e.InnerException with
| :? TaskCanceledException as tc -> return Result.Cancelled
| _ -> return Result.Err (e.Flatten())
| :? OperationCanceledException as e -> return Result.Cancelled
| _ as e -> return Result.Err e
}
let canceltask = new CancellableTaskBuilder()
let cts = new CancellationTokenSource()
let t =
canceltask {
let! r = Task.Factory.StartNew(fun t -> "Task 1")
cts.Cancel()
let! r1 = Task.Factory.StartNew(fun t -> "Task 2")
return r + "_" + r1
}
canceltask.Start(t, CancelToken.Create(cts))
async {
let! result = canceltask.ToAsync(t, CancelToken.Create(cts))
printfn "%A" result
} |> Async.Start
cts.Cancel()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment