@@ -19,77 +19,91 @@ namespace Internal
1919namespace IO
2020namespace Async
2121
22+ /-!
23+ # Asynchronous Context
24+
25+ This module provides `ContextAsync`, a monad for asynchronous computations with built-in
26+ cancellation support.
27+ -/
28+
2229/--
23- An asynchronous computation with cancellation support via a `Context`.
30+ An asynchronous computation with cancellation support via a `Context`. `ContextAsync α` is equivalent
31+ to `ReaderT Context Async α`, providing a `Context` value through async computations.
2432-/
2533@[expose] abbrev ContextAsync (α : Type ) := ReaderT Context Async α
2634
2735namespace ContextAsync
2836
2937/--
30- Run a `ContextAsync` computation with a given context.
38+ Runs a `ContextAsync` computation with a given context. Typically the context is created with
39+ `Context.new` or `Context.fork`.
3140-/
3241@[inline]
3342protected def run (ctx : Context) (x : ContextAsync α) : Async α :=
3443 x ctx
3544
3645/--
37- Create a `ContextAsync` from an `Async` computation.
46+ Lifts an `Async` computation into `ContextAsync`.
47+ The lifted computation ignores the context and won't respect cancellation.
3848-/
3949@[inline]
4050protected def lift (x : Async α) : ContextAsync α :=
4151 fun _ => x
4252
4353/--
44- Get the current context.
54+ Returns the current context for inspection or to pass to other functions .
4555-/
4656@[inline]
4757def getContext : ContextAsync Context :=
4858 fun ctx => pure ctx
4959
5060/--
51- Fork a child context and run a computation within it.
61+ Forks a child context, runs a computation within it, and cancels the child context afterwards.
62+ The child inherits parent's cancellation, but cancelling the child doesn't affect the parent.
5263-/
5364@[inline]
5465def fork (x : ContextAsync α) : ContextAsync α :=
5566 fun parent => do
5667 let child ← Context.fork parent
57- x child
68+ try x child finally child.cancel .cancel
5869
5970/--
60- Check if the current context is cancelled.
71+ Checks if the current context is cancelled. Returns `true` if the context (or any ancestor) has been cancelled.
72+ Long-running operations should periodically check this and exit gracefully when cancelled.
6173-/
6274@[inline]
6375def isCancelled : ContextAsync Bool := do
6476 let ctx ← getContext
6577 ctx.isCancelled
6678
6779/--
68- Get the cancellation reason if the context is cancelled.
80+ Gets the cancellation reason if the context is cancelled. Returns `some reason` if cancelled, `none` otherwise,
81+ allowing you to distinguish between different cancellation types.
6982-/
7083@[inline]
7184def getCancellationReason : ContextAsync (Option CancellationReason) := do
7285 let ctx ← getContext
7386 ctx.getCancellationReason
7487
7588/--
76- Cancel the current context with the given reason.
89+ Cancels the current context with the given reason, cascading to all child contexts.
90+ Cancellation is cooperative, operations must explicitly check `isCancelled` or use `awaitCancellation` to respond.
7791-/
7892@[inline]
7993def cancel (reason : CancellationReason) : ContextAsync Unit := do
8094 let ctx ← getContext
8195 ctx.cancel reason
8296
8397/--
84- Wait for the current context to be cancelled.
98+ Returns a selector that completes when the current context is cancelled.
8599-/
86100@[inline]
87101def doneSelector : ContextAsync (Selector Unit) := do
88102 let ctx ← getContext
89103 return ctx.doneSelector
90104
91105/--
92- Wait for the current context to be cancelled.
106+ Waits for the current context to be cancelled.
93107-/
94108@[inline]
95109def awaitCancellation : ContextAsync Unit := do
@@ -98,8 +112,8 @@ def awaitCancellation : ContextAsync Unit := do
98112 await task
99113
100114/--
101- Run two computations concurrently and return both results. If either fails or is cancelled,
102- both are cancelled.
115+ Runs two computations concurrently and returns both results.
116+ If either fails or is cancelled, both are cancelled immediately and the exception is propagated .
103117-/
104118@[inline, specialize]
105119def concurrently (x : ContextAsync α) (y : ContextAsync β)
@@ -108,8 +122,8 @@ def concurrently (x : ContextAsync α) (y : ContextAsync β)
108122 Async.concurrently (x ctx) (y ctx) prio
109123
110124/--
111- Run two computations concurrently and return the result of the first to complete.
112- The loser's context is cancelled.
125+ Runs two computations concurrently and returns the result of the first to complete.
126+ Each runs in its own child context; when either completes, the other is cancelled immediately .
113127-/
114128@[inline, specialize]
115129def race [Inhabited α] (x : ContextAsync α) (y : ContextAsync α)
@@ -129,8 +143,8 @@ def race [Inhabited α] (x : ContextAsync α) (y : ContextAsync α)
129143 pure result
130144
131145/--
132- Run all computations concurrently and collect results. If any fails or is cancelled,
133- all are cancelled.
146+ Runs all computations concurrently and collects results in the same order.
147+ If any computation fails, all others are cancelled and the exception is propagated .
134148-/
135149@[inline, specialize]
136150def concurrentlyAll (xs : Array (ContextAsync α))
@@ -139,9 +153,9 @@ def concurrentlyAll (xs : Array (ContextAsync α))
139153 Async.concurrentlyAll (xs.map (· ctx)) prio
140154
141155/--
142- Run all computations concurrently and return the first result. All losers are cancelled.
156+ Runs all computations concurrently and returns the first result.
157+ Each computation gets its own child context; the first successful result wins and all others are cancelled.
143158-/
144- @[inline, specialize]
145159def raceAll [ForM ContextAsync c (ContextAsync α)] (xs : c)
146160 (prio := Task.Priority.default) : ContextAsync α := do
147161 let parent ← getContext
@@ -150,27 +164,27 @@ def raceAll [ForM ContextAsync c (ContextAsync α)] (xs : c)
150164 ForM.forM xs fun x => do
151165 let ctx ← Context.fork parent
152166 let task ← async (x ctx) prio
167+
153168 background do
154169 try
155170 let result ← await task
156- -- Cancel parent to stop other tasks
157- parent.cancel .cancel
158171 promise.resolve (.ok result)
159172 catch e =>
160- -- Only set error if promise not already resolved
161173 discard $ promise.resolve (.error e)
162174
163175 let result ← await promise
176+ parent.cancel .cancel
164177 Async.ofExcept result
165178
166179/--
167- Run a computation in the background with its own forked context.
180+ Runs a computation in the background with its own forked context, returning immediately.
181+ The task continues even after the parent completes but is cancelled when the parent context is cancelled.
168182-/
169183@[inline]
170184def background (x : ContextAsync α) (prio := Task.Priority.default) : ContextAsync Unit := do
171185 let parent ← getContext
172186 let child ← Context.fork parent
173- Async.background (x child) prio
187+ Async.background (x child *> child.cancel .cancel ) prio
174188
175189instance : Functor ContextAsync where
176190 map f x := fun ctx => f <$> x ctx
0 commit comments