Last active
November 4, 2023 19:56
-
-
Save daveyostcom/72ce3dd8fa00c0d3885d707238aaa3ff to your computer and use it in GitHub Desktop.
SemaphoreSlim WaitAsync with CancellationToken
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System.Threading | |
open System.Threading.Tasks | |
let semaphoreWaitAsync semaphore cancellationToken = task { | |
printfn "Semaphore wait begin." | |
let ss = (semaphore: SemaphoreSlim) | |
let ct = (cancellationToken : CancellationToken) | |
try | |
do! ss.WaitAsync(ct) // This doesn’t work. OperationCanceledException is not caught. | |
// ss.WaitAsync(ct) |> Task.WaitAll // This doesn’t work. AggregateException is not caught. | |
// ss.WaitAsync(ct) |> ignore // This doesn’t work. It returns immediately. | |
printfn "semaphoreWaitAsync result: true" | |
return true | |
with | |
// | :? AggregateException as ae when ae.InnerExceptions.Count > 0 -> | |
// ae.InnerExceptions | |
// |> List.ofSeq | |
// |> List.map (fun e -> $"Caught exception: {e.Message}") | |
// |> String.concat "\n" | |
// |> printfn "semaphoreWaitAsync caught AggregateExceptions:\n%s" | |
// printfn "semaphoreWaitAsync result: false" | |
// return false | |
// | :? OperationCanceledException as e -> | |
// printfn "semaphoreWaitAsync caught %A" e | |
// printfn "semaphoreWaitAsync result: false" | |
// return false | |
| e -> | |
printfn "semaphoreWaitAsync caught %A" e | |
printfn "semaphoreWaitAsync result: false" | |
return false } | |
let releaseAfterDelay runSec semaphore = | |
let worker() = | |
printfn $"Task started. Waiting for {runSec} seconds before releasing the semaphore..." | |
task { do! Task.Delay(runSec * 1000) } |> Task.WaitAll | |
let s = (semaphore: SemaphoreSlim) | |
s.Release() |> ignore | |
printfn "releaseAfterDelay done" | |
Task.Run(worker) | |
let cancelAfterDelay cancelSec cts = | |
let worker() = | |
task { do! Task.Delay(cancelSec * 1000) } |> Task.WaitAll | |
printfn "Cancelling." | |
(cts: CancellationTokenSource).Cancel() | |
printfn "cancelAfterDelay done" | |
Task.Run(worker) | |
let runAndCancel runSec cancelSec = task { | |
let semaphore = new SemaphoreSlim(0, 1) | |
let cts = new CancellationTokenSource() | |
let a = releaseAfterDelay runSec semaphore | |
let b = cancelAfterDelay cancelSec cts | |
let! ok = semaphoreWaitAsync semaphore cts.Token | |
let tasks = [| a ; b |] | |
Task.WaitAll(tasks) | |
printfn $"runAndCancel: {ok}" } | |
let waitForever() = task { | |
let semaphore = new SemaphoreSlim(0, 1) | |
let cts = new CancellationTokenSource() | |
let! ok = semaphoreWaitAsync semaphore cts.Token | |
printfn $"waitForever {ok}" } | |
[<EntryPoint>] | |
let main _ = | |
runAndCancel 4 2 |> Task.WaitAll | |
printfn "" | |
runAndCancel 2 4 |> Task.WaitAll | |
printfn "" | |
waitForever() |> Task.WaitAll | |
0 | |
(* Output: | |
Semaphore wait begin. | |
Task started. Waiting for 4 seconds before releasing the semaphore... | |
Cancelling. | |
cancelAfterDelay done | |
semaphoreWaitAsync caught System.OperationCanceledException: The operation was canceled. | |
at System.Threading.CancellationToken.ThrowOperationCanceledException() | |
at System.Threading.SemaphoreSlim.WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, Int32 millisecondsTimeout, CancellationToken cancellationToken) | |
at [email protected]() in /tmp/SemaphoreWaitAsync/Program.fs:line 10 | |
semaphoreWaitAsync result: false | |
releaseAfterDelay done | |
runAndCancel: False | |
Semaphore wait begin. | |
Task started. Waiting for 2 seconds before releasing the semaphore... | |
releaseAfterDelay done | |
semaphoreWaitAsync result: true | |
Cancelling. | |
cancelAfterDelay done | |
runAndCancel: True | |
Semaphore wait begin. | |
*) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment