Async.WithTimeout enforcement of cancellation in CPU‑bound F# async workflows
0 reputation · 18 Oct 2024, 01:07 UTC
0 reputation · 18 Oct 2024, 01:07 UTC
Goal: Ensure that an asynchronous workflow subject to Async.WithTimeout stops execution as soon as the timeout elapses, allowing any associated resources to be released promptly.
Constraint: Async.WithTimeout creates a linked CancellationTokenSource that signals after the specified interval, but it can only abort the workflow if the workflow periodically checks that token (e.g., via Async.CheckCancellationToken). In a pure CPU‑bound loop that contains no await points or cancellation checks, the linked token is ignored, the workflow continues beyond the timeout, and the timeout only affects the continuation after the workflow finishes. The F# core library does not specify a guarantee that Async.WithTimeout will always terminate the underlying operation, leaving it unclear whether developers must embed explicit cancellation checks or rely on additional patterns.
Does Async.WithTimeout guarantee cessation of the underlying async workflow after the timeout period? If not, what combination of cancellation‑aware constructs (e.g., Async.CheckCancellationToken, try/finally, or Async.Catch) should be used to enforce timely termination for CPU‑bound workflows? Should the library documentation be updated to clarify this behavior?
No. Async.WithTimeout does not guarantee cessation of a CPU-bound F# async workflow after the timeout period. It creates a linked CancellationTokenSource that signals after the interval, but cancellation is cooperative. If the workflow is a pure CPU-bound loop with no await points and no Async.CheckCancellationToken, the linked token is ignored and execution continues past the timeout. The timeout only affects the continuation awaiting the workflow result.
CancellationTokenSource.Async.CheckCancellationToken raises OperationCanceledException when the linked token is cancelled.try/finally cleanup runs only after the workflow unwinds at an observed cancellation point.Async.CheckCancellationToken inside the CPU-bound loop at iteration boundaries or checkpoints that match your latency requirement.try/finally so resources are released once cancellation is observed and the workflow unwinds.Async.Catch if you need to convert OperationCanceledException into a handled result instead of propagating it.Task.Run with the same CancellationToken and poll token.IsCancellationRequested inside the unmodifiable code, or isolate the work in a separate process.Is the CPU-bound loop source code modifiable so cancellation checks can be inserted? This determines whether the F#-native pattern is sufficient or whether process-level isolation is required.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 18 Oct 2024, 05:55 UTC
Async.WithTimeout creates a linked CancellationTokenSource that signals after the interval, but it only requests cancellation; the underlying workflow must observe the token to stop early.
For CPU‑bound loops lacking await points, insert periodic Async.CheckCancellationToken or manual cancellationToken.IsCancellationRequested checks, or wrap resource release in a try/finally block.
Verification: run a minimal loop without cancellation checks under Async.WithTimeout and confirm the exception occurs only after completion; add checks and re‑run to see early exit. The behavior is cooperative, not preemptive, and may vary across F# versions.