Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit d4be6de

Browse files
committed
add plumbing for subtask.cancel intrinisic
Signed-off-by: Joel Dice <joel.dice@fermyon.com> add plumbing for `task.cancel` intrinsic Signed-off-by: Joel Dice <joel.dice@fermyon.com> draft `task.cancel` and `subtask.cancel` implementations Tests to follow in a subsequent commit. This includes some refactoring to reuse the code for aborting spawned tasks in the case of guest->host calls. Signed-off-by: Joel Dice <joel.dice@fermyon.com> `subtask.cancel`/`task.cancel` tests (WIP) Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent c3214ed commit d4be6de

28 files changed

Lines changed: 1017 additions & 207 deletions

File tree

Cargo.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cranelift/src/compiler/component.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ impl<'a> TrampolineCompiler<'a> {
108108
Trampoline::TaskReturn { results, options } => {
109109
self.translate_task_return_call(*results, options)
110110
}
111+
Trampoline::TaskCancel { instance } => self.translate_task_cancel_call(*instance),
111112
Trampoline::WaitableSetNew { instance } => self.translate_waitable_set_new(*instance),
112113
Trampoline::WaitableSetWait {
113114
instance,
@@ -133,6 +134,9 @@ impl<'a> TrampolineCompiler<'a> {
133134
Trampoline::WaitableJoin { instance } => self.translate_waitable_join(*instance),
134135
Trampoline::Yield { async_ } => self.translate_yield_call(*async_),
135136
Trampoline::SubtaskDrop { instance } => self.translate_subtask_drop_call(*instance),
137+
Trampoline::SubtaskCancel { instance, async_ } => {
138+
self.translate_subtask_cancel_call(*instance, *async_)
139+
}
136140
Trampoline::StreamNew { ty } => self.translate_future_or_stream_call(
137141
&[ty.as_u32()],
138142
None,
@@ -650,6 +654,25 @@ impl<'a> TrampolineCompiler<'a> {
650654
);
651655
}
652656

657+
fn translate_task_cancel_call(&mut self, caller_instance: RuntimeComponentInstanceIndex) {
658+
let args = self.builder.func.dfg.block_params(self.block0).to_vec();
659+
let vmctx = args[0];
660+
661+
let callee_args = vec![
662+
vmctx,
663+
self.builder
664+
.ins()
665+
.iconst(ir::types::I32, i64::from(caller_instance.as_u32())),
666+
];
667+
668+
self.translate_intrinsic_libcall(
669+
vmctx,
670+
host::task_cancel,
671+
&callee_args,
672+
TrapSentinel::Falsy,
673+
);
674+
}
675+
653676
fn translate_task_wait_or_poll_call(
654677
&mut self,
655678
caller_instance: RuntimeComponentInstanceIndex,
@@ -1180,6 +1203,33 @@ impl<'a> TrampolineCompiler<'a> {
11801203
);
11811204
}
11821205

1206+
fn translate_subtask_cancel_call(
1207+
&mut self,
1208+
caller_instance: RuntimeComponentInstanceIndex,
1209+
async_: bool,
1210+
) {
1211+
let args = self.builder.func.dfg.block_params(self.block0).to_vec();
1212+
let vmctx = args[0];
1213+
let mut callee_args = vec![
1214+
vmctx,
1215+
self.builder
1216+
.ins()
1217+
.iconst(ir::types::I32, i64::from(caller_instance.as_u32())),
1218+
self.builder
1219+
.ins()
1220+
.iconst(ir::types::I8, if async_ { 1 } else { 0 }),
1221+
];
1222+
1223+
callee_args.extend(args[2..].iter().copied());
1224+
1225+
self.translate_intrinsic_libcall(
1226+
vmctx,
1227+
host::subtask_cancel,
1228+
&callee_args,
1229+
TrapSentinel::NegativeOne,
1230+
);
1231+
}
1232+
11831233
fn load_optional_memory(
11841234
&mut self,
11851235
vmctx: ir::Value,

crates/environ/src/component.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ macro_rules! foreach_builtin_component_function {
8888
#[cfg(feature = "component-model-async")]
8989
task_return(vmctx: vmctx, ty: u32, memory: ptr_u8, string_encoding: u8, storage: ptr_u8, storage_len: size) -> bool;
9090
#[cfg(feature = "component-model-async")]
91+
task_cancel(vmctx: vmctx, caller_instance: u32) -> bool;
92+
#[cfg(feature = "component-model-async")]
9193
waitable_set_new(vmctx: vmctx, caller_instance: u32) -> u64;
9294
#[cfg(feature = "component-model-async")]
9395
waitable_set_wait(vmctx: vmctx, caller_instance: u32, async_: u8, memory: ptr_u8, set: u32, payload: u32) -> u64;
@@ -102,13 +104,15 @@ macro_rules! foreach_builtin_component_function {
102104
#[cfg(feature = "component-model-async")]
103105
subtask_drop(vmctx: vmctx, caller_instance: u32, task_id: u32) -> bool;
104106
#[cfg(feature = "component-model-async")]
105-
sync_enter(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, task_return_type: u32, string_encoding: u32, result_count: u32, storage: ptr_u8, storage_len: size) -> bool;
107+
subtask_cancel(vmctx: vmctx, caller_instance: u32, async_: u8, task_id: u32) -> u64;
108+
#[cfg(feature = "component-model-async")]
109+
sync_enter(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, callee_instance: u32, task_return_type: u32, string_encoding: u32, result_count: u32, storage: ptr_u8, storage_len: size) -> bool;
106110
#[cfg(feature = "component-model-async")]
107-
sync_exit(vmctx: vmctx, callback: ptr_u8, caller_instance: u32, callee: ptr_u8, callee_instance: u32, param_count: u32, storage: ptr_u8, storage_len: size) -> bool;
111+
sync_exit(vmctx: vmctx, callback: ptr_u8, callee: ptr_u8, param_count: u32, storage: ptr_u8, storage_len: size) -> bool;
108112
#[cfg(feature = "component-model-async")]
109-
async_enter(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, task_return_type: u32, string_encoding: u32, params: u32, results: u32) -> bool;
113+
async_enter(vmctx: vmctx, memory: ptr_u8, start: ptr_u8, return_: ptr_u8, caller_instance: u32, callee_instance: u32, task_return_type: u32, string_encoding: u32, params: u32, results: u32) -> bool;
110114
#[cfg(feature = "component-model-async")]
111-
async_exit(vmctx: vmctx, callback: ptr_u8, post_return: ptr_u8, caller_instance: u32, callee: ptr_u8, callee_instance: u32, param_count: u32, result_count: u32, flags: u32) -> u64;
115+
async_exit(vmctx: vmctx, callback: ptr_u8, post_return: ptr_u8, callee: ptr_u8, param_count: u32, result_count: u32, flags: u32) -> u64;
112116
#[cfg(feature = "component-model-async")]
113117
future_new(vmctx: vmctx, ty: u32) -> u64;
114118
#[cfg(feature = "component-model-async")]

crates/environ/src/component/dfg.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ pub enum Trampoline {
291291
results: TypeTupleIndex,
292292
options: CanonicalOptions,
293293
},
294+
TaskCancel {
295+
instance: RuntimeComponentInstanceIndex,
296+
},
294297
WaitableSetNew {
295298
instance: RuntimeComponentInstanceIndex,
296299
},
@@ -316,6 +319,10 @@ pub enum Trampoline {
316319
SubtaskDrop {
317320
instance: RuntimeComponentInstanceIndex,
318321
},
322+
SubtaskCancel {
323+
instance: RuntimeComponentInstanceIndex,
324+
async_: bool,
325+
},
319326
StreamNew {
320327
ty: TypeStreamTableIndex,
321328
},
@@ -795,6 +802,9 @@ impl LinearizeDfg<'_> {
795802
results: *results,
796803
options: self.options(options),
797804
},
805+
Trampoline::TaskCancel { instance } => info::Trampoline::TaskCancel {
806+
instance: *instance,
807+
},
798808
Trampoline::WaitableSetNew { instance } => info::Trampoline::WaitableSetNew {
799809
instance: *instance,
800810
},
@@ -826,6 +836,10 @@ impl LinearizeDfg<'_> {
826836
Trampoline::SubtaskDrop { instance } => info::Trampoline::SubtaskDrop {
827837
instance: *instance,
828838
},
839+
Trampoline::SubtaskCancel { instance, async_ } => info::Trampoline::SubtaskCancel {
840+
instance: *instance,
841+
async_: *async_,
842+
},
829843
Trampoline::StreamNew { ty } => info::Trampoline::StreamNew { ty: *ty },
830844
Trampoline::StreamRead { ty, options } => info::Trampoline::StreamRead {
831845
ty: *ty,

crates/environ/src/component/info.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,14 @@ pub enum Trampoline {
721721
options: CanonicalOptions,
722722
},
723723

724+
/// A `task.cancel` intrinsic, which acknowledges a `CANCELLED` event
725+
/// delivered to a guest task previously created by a call to an async
726+
/// export.
727+
TaskCancel {
728+
/// The specific component instance which is calling the intrinsic.
729+
instance: RuntimeComponentInstanceIndex,
730+
},
731+
724732
/// A `waitable-set.new` intrinsic.
725733
WaitableSetNew {
726734
/// The specific component instance which is calling the intrinsic.
@@ -776,6 +784,15 @@ pub enum Trampoline {
776784
instance: RuntimeComponentInstanceIndex,
777785
},
778786

787+
/// A `subtask.cancel` intrinsic to drop an in-progress task.
788+
SubtaskCancel {
789+
/// The specific component instance which is calling the intrinsic.
790+
instance: RuntimeComponentInstanceIndex,
791+
/// If `false`, block until cancellation completes rather than return
792+
/// `BLOCKED`.
793+
async_: bool,
794+
},
795+
779796
/// A `stream.new` intrinsic to create a new `stream` handle of the
780797
/// specified type.
781798
StreamNew {
@@ -1043,13 +1060,15 @@ impl Trampoline {
10431060
ResourceDrop(i) => format!("component-resource-drop[{}]", i.as_u32()),
10441061
BackpressureSet { .. } => format!("backpressure-set"),
10451062
TaskReturn { .. } => format!("task-return"),
1063+
TaskCancel { .. } => format!("task-cancel"),
10461064
WaitableSetNew { .. } => format!("waitable-set-new"),
10471065
WaitableSetWait { .. } => format!("waitable-set-wait"),
10481066
WaitableSetPoll { .. } => format!("waitable-set-poll"),
10491067
WaitableSetDrop { .. } => format!("waitable-set-drop"),
10501068
WaitableJoin { .. } => format!("waitable-join"),
10511069
Yield { .. } => format!("yield"),
10521070
SubtaskDrop { .. } => format!("subtask-drop"),
1071+
SubtaskCancel { .. } => format!("subtask-cancel"),
10531072
StreamNew { .. } => format!("stream-new"),
10541073
StreamRead { .. } => format!("stream-read"),
10551074
StreamWrite { .. } => format!("stream-write"),

crates/environ/src/component/translate.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ enum LocalInitializer<'data> {
196196
result: Option<ComponentValType>,
197197
options: LocalCanonicalOptions,
198198
},
199+
TaskCancel {
200+
func: ModuleInternedTypeIndex,
201+
},
199202
WaitableSetNew {
200203
func: ModuleInternedTypeIndex,
201204
},
@@ -222,6 +225,10 @@ enum LocalInitializer<'data> {
222225
SubtaskDrop {
223226
func: ModuleInternedTypeIndex,
224227
},
228+
SubtaskCancel {
229+
func: ModuleInternedTypeIndex,
230+
async_: bool,
231+
},
225232
StreamNew {
226233
ty: ComponentDefinedTypeId,
227234
func: ModuleInternedTypeIndex,
@@ -675,6 +682,11 @@ impl<'a, 'data> Translator<'a, 'data> {
675682
options,
676683
}
677684
}
685+
wasmparser::CanonicalFunction::TaskCancel => {
686+
let func = self.core_func_signature(core_func_index)?;
687+
core_func_index += 1;
688+
LocalInitializer::TaskCancel { func }
689+
}
678690
wasmparser::CanonicalFunction::WaitableSetNew => {
679691
let func = self.core_func_signature(core_func_index)?;
680692
core_func_index += 1;
@@ -718,6 +730,11 @@ impl<'a, 'data> Translator<'a, 'data> {
718730
core_func_index += 1;
719731
LocalInitializer::SubtaskDrop { func }
720732
}
733+
wasmparser::CanonicalFunction::SubtaskCancel { async_ } => {
734+
let func = self.core_func_signature(core_func_index)?;
735+
core_func_index += 1;
736+
LocalInitializer::SubtaskCancel { func, async_ }
737+
}
721738
wasmparser::CanonicalFunction::StreamNew { ty } => {
722739
let ty = types.component_defined_type_at(ty);
723740
let func = self.core_func_signature(core_func_index)?;

crates/environ/src/component/translate/inline.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,15 @@ impl<'a> Inliner<'a> {
698698
.push((*func, dfg::Trampoline::TaskReturn { results, options }));
699699
frame.funcs.push(dfg::CoreDef::Trampoline(index));
700700
}
701+
TaskCancel { func } => {
702+
let index = self.result.trampolines.push((
703+
*func,
704+
dfg::Trampoline::TaskCancel {
705+
instance: frame.instance,
706+
},
707+
));
708+
frame.funcs.push(dfg::CoreDef::Trampoline(index));
709+
}
701710
WaitableSetNew { func } => {
702711
let index = self.result.trampolines.push((
703712
*func,
@@ -775,6 +784,16 @@ impl<'a> Inliner<'a> {
775784
));
776785
frame.funcs.push(dfg::CoreDef::Trampoline(index));
777786
}
787+
SubtaskCancel { func, async_ } => {
788+
let index = self.result.trampolines.push((
789+
*func,
790+
dfg::Trampoline::SubtaskCancel {
791+
instance: frame.instance,
792+
async_: *async_,
793+
},
794+
));
795+
frame.funcs.push(dfg::CoreDef::Trampoline(index));
796+
}
778797
StreamNew { ty, func } => {
779798
let InterfaceType::Stream(ty) =
780799
types.defined_type(frame.translation.types_ref(), *ty)?

0 commit comments

Comments
 (0)