Skip to content

CABI: tighten subtask.cancel behavior again, add more tests - #726

Merged
lukewagner merged 1 commit into
mainfrom
tweak-cancel
Sep 23, 2026
Merged

lukewagner merged 1 commit into
mainfrom
tweak-cancel

Conversation

@lukewagner

Copy link
Copy Markdown
Member

This is a further refinement of #723 based on some discussion with @dicej. To keep things simple, testable and symmetric with how async lower works, subtask.cancel is returned to it's pre-#716 behavior where the only thread resumed during subtask.cancel is an implicit callback thread that's waiting in its event loop (which is the only way to receive TASK_CANCELLED atm) and then control flow deterministically transfers back as soon as the callback exits or blocks. This PR also adds a bunch of tests to confirm that nothing else is run instead of or after, and that, while running, coop-thread-switching is allowed.

dicej added a commit to dicej/wasmtime that referenced this pull request Sep 22, 2026
WebAssembly/component-model#726 refined the behavior of
`subtask.cancel` to more closely match how async-lowered calls behave, meaning
the runtime must resume the cancelling thread specifically as soon as the
cancellee suspends or exits.

This required some refactoring, and I believe it's a net simplification of how
the runtime decides which thread to switch to and when.

- Both guest->guest calls and `subtask.cancel` use a new
  `SuspendReason::YieldingToSubtask` variant which replaces
  `WaitingForGuestSubtask` and means "I'm yielding specifically to the specified
  (via `ConcurrentState::switch_item`) subtask and expect to be resumed when
  that subtask suspends or exits."

- I've removed `GuestTask::switch_item` in favor of a new
  `ConcurrentState::next_switch_item` field.  This addresses a case exercised by
  the new `cancel-resumed-callback-switch.wast` test where a subtask, while
  being cancelled, explicitly resumes a thread belonging to a different task,
  which then suspends, in which case we need to resume the cancelling thread.
  This new `next_switch_item` field is saved and restored as needed so that a
  stack of cancels and guest->guest calls can each yield to their respective
  subtasks and return to their respective callers/cancellers without clobbering
  each other.

- The new `cancel-targeted-resume.wast` test revealed that Wasmtime was out of
  compliance regarding `CALLBACK_CODE_YIELD`; when a task is cancelled while it
  is stacklessly yielding, we must promote it so the `cancel` event can be
  delivered deterministically.  I've addressed this by defining a new
  `WakeOnCancel` enum type which can represent a thread which is waiting
  cancellably, yielding cancellably, or neither.

Since the aforementioned spec PR has not yet been merged as of this writing, I
haven't updated the `tests/component-model` submodule yet, but I've verified
locally that the new tests pass.
dicej added a commit to dicej/wasmtime that referenced this pull request Sep 22, 2026
WebAssembly/component-model#726 refined the behavior of
`subtask.cancel` to more closely match how async-lowered calls behave, meaning
the runtime must resume the cancelling thread specifically as soon as the
cancellee suspends or exits.

This required some refactoring, and I believe it's a net simplification of how
the runtime decides which thread to switch to and when.

- Both guest->guest calls and `subtask.cancel` use a new
  `SuspendReason::YieldingToSubtask` variant which replaces
  `WaitingForGuestSubtask` and means "I'm yielding specifically to the specified
  (via `ConcurrentState::switch_item`) subtask and expect to be resumed when
  that subtask suspends or exits."

- I've removed `GuestTask::switch_item` in favor of a new
  `ConcurrentState::next_switch_item` field.  This addresses a case exercised by
  the new `cancel-resumed-callback-switch.wast` test where a subtask, while
  being cancelled, explicitly resumes a thread belonging to a different task,
  which then suspends, in which case we need to resume the cancelling thread.
  This new `next_switch_item` field is saved and restored as needed so that a
  stack of cancels and guest->guest calls can each yield to their respective
  subtasks and return to their respective callers/cancellers without clobbering
  each other.

- The new `cancel-targeted-resume.wast` test revealed that Wasmtime was out of
  compliance regarding `CALLBACK_CODE_YIELD`; when a task is cancelled while it
  is stacklessly yielding, we must promote it so the `cancel` event can be
  delivered deterministically.  I've addressed this by defining a new
  `WakeOnCancel` enum type which can represent a thread which is waiting
  cancellably, yielding cancellably, or neither.

Since the aforementioned spec PR has not yet been merged as of this writing, I
haven't updated the `tests/component-model` submodule yet, but I've verified
locally that the new tests pass.
pull Bot pushed a commit to langyo/wasmtime that referenced this pull request Sep 23, 2026
…tecodealliance#14382)

WebAssembly/component-model#726 refined the behavior of
`subtask.cancel` to more closely match how async-lowered calls behave, meaning
the runtime must resume the cancelling thread specifically as soon as the
cancellee suspends or exits.

This required some refactoring, and I believe it's a net simplification of how
the runtime decides which thread to switch to and when.

- Both guest->guest calls and `subtask.cancel` use a new
  `SuspendReason::YieldingToSubtask` variant which replaces
  `WaitingForGuestSubtask` and means "I'm yielding specifically to the specified
  (via `ConcurrentState::switch_item`) subtask and expect to be resumed when
  that subtask suspends or exits."

- I've removed `GuestTask::switch_item` in favor of a new
  `ConcurrentState::next_switch_item` field.  This addresses a case exercised by
  the new `cancel-resumed-callback-switch.wast` test where a subtask, while
  being cancelled, explicitly resumes a thread belonging to a different task,
  which then suspends, in which case we need to resume the cancelling thread.
  This new `next_switch_item` field is saved and restored as needed so that a
  stack of cancels and guest->guest calls can each yield to their respective
  subtasks and return to their respective callers/cancellers without clobbering
  each other.

- The new `cancel-targeted-resume.wast` test revealed that Wasmtime was out of
  compliance regarding `CALLBACK_CODE_YIELD`; when a task is cancelled while it
  is stacklessly yielding, we must promote it so the `cancel` event can be
  delivered deterministically.  I've addressed this by defining a new
  `WakeOnCancel` enum type which can represent a thread which is waiting
  cancellably, yielding cancellably, or neither.

Since the aforementioned spec PR has not yet been merged as of this writing, I
haven't updated the `tests/component-model` submodule yet, but I've verified
locally that the new tests pass.
@lukewagner
lukewagner merged commit 2f1e56f into main Sep 23, 2026
2 checks passed
@lukewagner
lukewagner deleted the tweak-cancel branch September 23, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants