@@ -584,7 +584,8 @@ time before or after the callee returns. If the callee returns and the
584584` OnResolve ` callback has * not* yet been called, the caller may invoke the
585585returned ` OnCancel ` callback * at most once* to cooperatively request that the
586586callee "hurry up" and call ` OnResolve ` (possibly, but not necessarily, passing
587- ` None ` and/or skipping the call to ` OnStart ` ).
587+ ` None ` and/or skipping the call to ` OnStart ` ). The ` OnCancel ` may transitively
588+ execute arbitrary guest code but must not block.
588589
589590When ` FuncInst ` is implemented by wasm guest code (as opposed to the host), each
590591call creates a ` Task ` object to track the state of the call and ensure that the
@@ -730,12 +731,20 @@ returned a value to its caller.
730731```
731732
732733The ` Task.request_cancellation ` method implements the ` OnCancel ` callback
733- described above that allows a caller to indicate that they are no longer
734+ described above and allows a task's caller to indicate that they are no longer
734735interested in the return value. If a task's implicit thread is waiting to start
735736(in ` Task.enter_implicit_thread ` , defined above) due to backpressure, then it is
736- immediately cancelled without running any guest code. Otherwise, the pending
737- request is stored in the task state so that it can be picked up from the
738- ` async callback ` event loop via ` Task.deliver_pending_cancel ` .
737+ immediately cancelled without running any guest code. Otherwise, if any of the
738+ threads in the callee's component instance are ready to run, one is resumed
739+ (chosen nondeterministically if there are multiple). Furthermore, the host may
740+ nondeterministically continue resuming ready threads in the same component
741+ instance until either the subtask resolves or the host declares that
742+ cancellation has blocked. Note that setting ` Task.state ` to ` PENDING_CANCEL `
743+ makes any implicit ` callback ` thread contained by the task that is waiting in
744+ its event loop ` ready ` (as long as the component instance's ` exclusive_thread `
745+ lock is not held by some other implicit thread). Thus, in the best case,
746+ ` subtask.cancel ` directly calls the subtask's ` callback ` function, passing
747+ ` TASK_CANCELLED ` .
739748``` python
740749 def request_cancellation (self ):
741750 if self .state == Task.State.INITIAL :
@@ -745,7 +754,18 @@ request is stored in the task state so that it can be picked up from the
745754 else :
746755 assert (self .state == Task.State.STARTED )
747756 self .state = Task.State.PENDING_CANCEL
757+ while self .state != Task.State.RESOLVED :
758+ candidates = { t for t in self .inst.threads if t.ready() }
759+ if candidates:
760+ random.choice(list (candidates)).resume()
761+ if not candidates or DETERMINISTIC_PROFILE or random.randint(0 ,1 ):
762+ break
763+ ```
748764
765+ If the pending cancellation request is not delivered to the subtask during
766+ ` Task.request_cancellation ` , it may still be delivered in the future to a
767+ ` callback ` via ` Task.deliver_pending_cancel ` :
768+ ``` python
749769 def has_pending_cancel (self ):
750770 return self .state == Task.State.PENDING_CANCEL
751771
@@ -4089,24 +4109,8 @@ validation specifies:
40894109* ` $f ` is given type ` (func (param i32) (result i32)) `
40904110* 🚝 - ` async ` is allowed (otherwise it must be absent)
40914111
4092- Calling ` $f ` sends a request to a nondeterministically-chosen thread of the
4093- subtask at the given index to cancel the subtask ASAP. This request is
4094- cooperative and the subtask may take arbitrarily long to receive and confirm
4095- the request. If the subtask doesn't immediately confirm the cancellation
4096- request, ` subtask.cancel ` returns ` BLOCKED ` and the caller must wait for a
4097- ` SUBTASK ` progress update using ` waitable-set ` methods as usual.
4098-
4099- When cancellation is confirmed the supertask will receive the final state of
4100- the subtask which is one of:
4101- * ` RETURNED ` , if the subtask successfully returned a value via ` task.return ` ;
4102- * ` CANCELLED_BEFORE_STARTED ` , if the subtask was cancelled before receiving its
4103- arguments (and thus no ` own ` handles were transferred); or
4104- * ` CANCELLED_BEFORE_RETURNED ` , if the subtask called ` task.cancel ` instead of
4105- ` task.return ` .
4106-
4107- This state is either returned by ` subtask.cancel ` , if the subtask resolved
4108- without blocking, or, if ` subtask.cancel ` returns ` BLOCKED ` , then as part of
4109- the event payload of a future ` SUBTASK ` event.
4112+ Calling ` $f ` invokes the following function which cooperatively requests
4113+ cancellation of the given subtask:
41104114``` python
41114115BLOCKED = 0x ffff_ffff
41124116
@@ -4122,11 +4126,8 @@ def canon_subtask_cancel(async_, i):
41224126 subtask.cancellation_requested = True
41234127 subtask.has_sync_waiter = True
41244128 subtask.on_cancel()
4125- if not subtask.resolved():
4126- if not async_:
4127- thread.wait_until(subtask.resolved)
4128- else :
4129- thread.yield_()
4129+ if not async_ and not subtask.resolved():
4130+ thread.wait_until(subtask.resolved)
41304131 subtask.has_sync_waiter = False
41314132 if not subtask.resolved():
41324133 return [BLOCKED ]
@@ -4148,26 +4149,28 @@ trapping on entry to `subtask.cancel`.
41484149
41494150Otherwise, if the subtask is * not* already resolved, the subtask's ` OnCancel `
41504151callback is called to officially request cancellation. If the callee is another
4151- component, ` OnCancel ` is implemented by ` Task.request_cancellation ` which, as
4152- defined above, will immediately resolve a task that is blocked waiting to enter
4153- due to backpressure. Otherwise, the second ` if not subtask.resolved() ` handles
4154- the general case where core wasm code needs to run in the callee to receive and
4155- explicitly resolve the task (by calling ` task.cancel ` or ` task.return ` ).
4156-
4157- If ` subtask.cancel ` is called synchronously, the call blocks until the subtask
4158- resolves. If ` subtask.cancel ` is called asynchronously, rather than * blocking* ,
4159- ` Thread.yield_ ` is called to allow the host to nondeterministically schedule
4160- whatever thread it heuristically chooses in the hopes that a subtask thread will
4161- get to execute and quickly resolve the subtask, allowing ` subtask.cancel ` to
4162- complete eagerly. Otherwise, ` subtask.cancel ` returns ` BLOCKED ` (` -1 ` ) and the
4163- caller must wait for a ` SUBTASK ` event using a waitable set.
4152+ component, ` OnCancel ` is implemented by ` Task.request_cancellation ` (defined
4153+ above); which may execute arbitrary guest code in the callee before returning,
4154+ but may not block.
4155+
4156+ If the subtask is * not* eagerly resolved before ` OnCancel ` returns and
4157+ ` subtask.cancel ` is called with the ` async ` immediate, then ` subtask.cancel `
4158+ immediately returns the sentinel ` BLOCKED ` (` -1 ` ) value. Otherwise, if called
4159+ without ` async ` , ` subtask.cancel ` blocks until the subtask * is* resolved. Once
4160+ the subtask is resolved, the return value is the subtask's final ` State ` value
4161+ which is one of:
4162+ * ` RETURNED ` , if the subtask successfully returned a value via ` task.return ` ;
4163+ * ` CANCELLED_BEFORE_STARTED ` , if the subtask was cancelled before receiving its
4164+ arguments (and thus no ` own ` handles were transferred); or
4165+ * ` CANCELLED_BEFORE_RETURNED ` , if the subtask called ` task.cancel ` instead of
4166+ ` task.return ` .
41644167
41654168Lastly, a subtle race condition guarded by the above code is that during the
4166- calls to ` on_cancel ` , ` yield_ ` and ` wait_until ` , arbitrary code may run which
4167- can reenter the caller's component instance. By setting ` has_sync_waiter ` to
4168- true for the duration of these calls, ` subtask.cancel ` prevents other threads in
4169- the same component instance from "stealing" an event or otherwise invalidating
4170- the conditions guarded at the beginning of ` subtask.cancel ` .
4169+ calls to ` on_cancel ` and ` wait_until ` , arbitrary code may run which can reenter
4170+ the caller's component instance. By setting ` has_sync_waiter ` to true for the
4171+ duration of these calls, ` subtask.cancel ` prevents other threads in the same
4172+ component instance from "stealing" an event or otherwise invalidating the
4173+ conditions guarded at the beginning of ` subtask.cancel ` .
41714174
41724175
41734176### 🔀 ` canon subtask.drop `
0 commit comments