@@ -22,6 +22,7 @@ summary of the motivation and animated sketch of the design in action.
22
22
* [ Backpressure] ( #backpressure )
23
23
* [ Returning] ( #returning )
24
24
* [ Borrows] ( #borrows )
25
+ * [ Cancellation] ( #cancellation )
25
26
* [ Async ABI] ( #async-abi )
26
27
* [ Async Import ABI] ( #async-import-abi )
27
28
* [ Async Export ABI] ( #async-export-abi )
@@ -470,9 +471,9 @@ loop interleaving `stream.read`s (of the readable end passed for `in`) and
470
471
` stream.write ` s (of the writable end it ` stream.new ` ed) before exiting the
471
472
task.
472
473
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.
476
477
477
478
### Borrows
478
479
@@ -495,6 +496,55 @@ there can be multiple overlapping async tasks executing in a component
495
496
instance, a borrowed handle must track * which* task's ` num_borrow ` s was
496
497
incremented so that the correct counter can be decremented.
497
498
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.
498
548
499
549
## Async ABI
500
550
@@ -924,8 +974,6 @@ will be added in future chunks roughly in the order listed to complete the full
924
974
comes after:
925
975
* remove the temporary trap mentioned above that occurs when a ` read ` and
926
976
` 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
929
977
* zero-copy forwarding/splicing
930
978
* some way to say "no more elements are coming for a while"
931
979
* ` recursive ` function type attribute: allow a function to opt in to
@@ -958,6 +1006,8 @@ comes after:
958
1006
[ `context.set` ] : Explainer.md#-contextset
959
1007
[ `backpressure.set` ] : Explainer.md#-backpressureset
960
1008
[ `task.return` ] : Explainer.md#-taskreturn
1009
+ [ `task.cancel` ] : Explainer.md#-taskcancel
1010
+ [ `subtask.cancel` ] : Explainer.md#-subtaskcancel
961
1011
[ `yield` ] : Explainer.md#-yield
962
1012
[ `waitable-set.wait` ] : Explainer.md#-waitable-setwait
963
1013
[ `waitable-set.poll` ] : Explainer.md#-waitable-setpoll
@@ -973,10 +1023,13 @@ comes after:
973
1023
[ `canon_backpressure_set` ] : CanonicalABI.md#-canon-backpressureset
974
1024
[ `canon_waitable_set_wait` ] : CanonicalABI.md#-canon-waitable-setwait
975
1025
[ `canon_task_return` ] : CanonicalABI.md#-canon-taskreturn
1026
+ [ `canon_task_cancel` ] : CanonicalABI.md#-canon-taskcancel
1027
+ [ `canon_subtask_cancel` ] : CanonicalABI.md#-canon-subtaskcancel
976
1028
[ `Task` ] : CanonicalABI.md#task-state
977
1029
[ `Task.enter` ] : CanonicalABI.md#task-state
978
1030
[ `Task.wait_on` ] : CanonicalABI.md#task-state
979
1031
[ `Waitable` ] : CanonicalABI.md#waitable-state
1032
+ [ `TASK_CANCELLED` ] : CanonicalABI.md#waitable-state
980
1033
[ `Task` ] : CanonicalABI.md#task-state
981
1034
[ `Subtask` ] : CanonicalABI.md#subtask-state
982
1035
[ Stream State ] : CanonicalABI.md#stream-state
0 commit comments