@@ -22,6 +22,7 @@ summary of the motivation and animated sketch of the design in action.
2222 * [ Backpressure] ( #backpressure )
2323 * [ Returning] ( #returning )
2424 * [ Borrows] ( #borrows )
25+ * [ Cancellation] ( #cancellation )
2526* [ Async ABI] ( #async-abi )
2627 * [ Async Import ABI] ( #async-import-abi )
2728 * [ Async Export ABI] ( #async-export-abi )
@@ -470,9 +471,9 @@ loop interleaving `stream.read`s (of the readable end passed for `in`) and
470471` stream.write ` s (of the writable end it ` stream.new ` ed) before exiting the
471472task.
472473
473- Once ` task.return ` is called, the task is in the "returned" state. A task can
474- only finish once it is in the "returned" state . See the [ ` canon_task_return ` ]
475- function in the Canonical ABI explainer for more details.
474+ Once ` task.return ` is called, the task is in the "returned" state and can
475+ finish execution any time thereafter . See the [ ` canon_task_return ` ] function in
476+ the Canonical ABI explainer for more details.
476477
477478### Borrows
478479
@@ -495,6 +496,55 @@ there can be multiple overlapping async tasks executing in a component
495496instance, a borrowed handle must track * which* task's ` num_borrow ` s was
496497incremented so that the correct counter can be decremented.
497498
499+ ### Cancellation
500+
501+ Once an async call has started, blocked and been added to the caller's table of
502+ waitables, the caller may decide that it no longer needs the results or effects
503+ of the subtask. In this case, the caller may ** cancel** the subtask by calling
504+ the [ ` subtask.cancel ` ] built-in.
505+
506+ Once cancellation is requested, since the subtask may have already racily
507+ returned a value, the caller may still receive a return value. However, the
508+ caller may also be notified that the subtask is in one of two additional
509+ terminal states:
510+ * the subtask was ** cancelled before it started** , in which case the caller's
511+ arguments were not passed to the callee (in particular, owned handles were
512+ not transferred); or
513+ * the subtask was ** cancelled before it returned** , in which case the arguments
514+ were passed, but no values were returned. However, all borrowed handles lent
515+ during the call have been dropped.
516+
517+ Thus there are * three* terminal states for a subtask: returned,
518+ cancelled-before-started and cancelled-before-returned. A subtask in one of
519+ these terminal states is said to be ** resolved** . A resolved subtask has always
520+ dropped all the borrowed handles that it was lent during the call.
521+
522+ As with the rest of async, cancellation is * cooperative* , allowing the subtask
523+ a chance to execute and clean up before it transitions to a resolved state (and
524+ relinquishes its borrowed handles). Since there are valid use cases where
525+ successful cancellation requires performing additional I/O using borrowed
526+ handles and potentially blocking in the process, the Component Model does not
527+ impose any limits on what a subtask can do after receiving a cancellation
528+ request nor is there a non-cooperative option to force termination (instead,
529+ this functionality would come as part of a future "[ blast zone] " feature).
530+ Thus, the ` subtask.cancel ` built-in can block and works just like an import
531+ call in that it can be called synchronously or asynchronously.
532+
533+ On the callee side of cancellation: when a caller requests cancellation via
534+ ` subtask.cancel ` , the callee receives a [ ` TASK_CANCELLED ` ] event (as produced
535+ by one of the ` waitable-set.{wait,poll} ` or ` yield ` built-ins or as received by
536+ the ` callback ` function). Upon receiving notice of cancellation, the callee can
537+ call the [ ` task.cancel ` ] built-in to resolve the subtask without returning a
538+ value. Alternatively, the callee can still call [ ` task.return ` ] as-if there
539+ were no cancellation. ` task.cancel ` doesn't take a value to return but does
540+ enforce the same [ borrow] ( #borrows ) rules as ` task.return ` . Ideally, a callee
541+ will ` task.cancel ` itself as soon as possible after receiving a
542+ ` TASK_CANCELLED ` event so that any caller waiting for the recovery of lent
543+ handles is unblocked ASAP. As with ` task.return ` , after calling ` task.cancel ` ,
544+ a callee can continue executing before exiting the task.
545+
546+ See the [ ` canon_subtask_cancel ` ] and [ ` canon_task_cancel ` ] functions in the
547+ Canonical ABI explainer for more details.
498548
499549## Async ABI
500550
@@ -924,8 +974,6 @@ will be added in future chunks roughly in the order listed to complete the full
924974comes after:
925975* remove the temporary trap mentioned above that occurs when a ` read ` and
926976 ` write ` of a stream/future happen from within the same component instance
927- * ` subtask.cancel ` : allow a supertask to signal to a subtask that its result is
928- no longer wanted and to please wrap it up promptly
929977* zero-copy forwarding/splicing
930978* some way to say "no more elements are coming for a while"
931979* ` recursive ` function type attribute: allow a function to opt in to
@@ -958,6 +1006,8 @@ comes after:
9581006[ `context.set` ] : Explainer.md#-contextset
9591007[ `backpressure.set` ] : Explainer.md#-backpressureset
9601008[ `task.return` ] : Explainer.md#-taskreturn
1009+ [ `task.cancel` ] : Explainer.md#-taskcancel
1010+ [ `subtask.cancel` ] : Explainer.md#-subtaskcancel
9611011[ `yield` ] : Explainer.md#-yield
9621012[ `waitable-set.wait` ] : Explainer.md#-waitable-setwait
9631013[ `waitable-set.poll` ] : Explainer.md#-waitable-setpoll
@@ -973,10 +1023,13 @@ comes after:
9731023[ `canon_backpressure_set` ] : CanonicalABI.md#-canon-backpressureset
9741024[ `canon_waitable_set_wait` ] : CanonicalABI.md#-canon-waitable-setwait
9751025[ `canon_task_return` ] : CanonicalABI.md#-canon-taskreturn
1026+ [ `canon_task_cancel` ] : CanonicalABI.md#-canon-taskcancel
1027+ [ `canon_subtask_cancel` ] : CanonicalABI.md#-canon-subtaskcancel
9761028[ `Task` ] : CanonicalABI.md#task-state
9771029[ `Task.enter` ] : CanonicalABI.md#task-state
9781030[ `Task.wait_on` ] : CanonicalABI.md#task-state
9791031[ `Waitable` ] : CanonicalABI.md#waitable-state
1032+ [ `TASK_CANCELLED` ] : CanonicalABI.md#waitable-state
9801033[ `Task` ] : CanonicalABI.md#task-state
9811034[ `Subtask` ] : CanonicalABI.md#subtask-state
9821035[ Stream State ] : CanonicalABI.md#stream-state
0 commit comments