Skip to content

Commit e06fbf7

Browse files
authored
Cooperative Multithreading (#11751)
* Initial work * Almost compiling * Partially working * Cleanup * Fix merge * Cancellation and suspension refactoring * Remove printlns * Test with several threads * More testing * Cancellation * Fix cancellation for explicit suspends * Finish cancellation test * Store threads in the instance table * Deletion almost there * Tests all pass * Tighten up task deletion * Set thread state correctly * Store pairs of thread and task ids * Remove lift abi members * Cleanup unnecessary change * More cleanup * Cleanup * Revert cargo changes * Revert cargo changes * Comments * Comments on test * Update comments * Update comments * Add space that was removed in an earlier commit * Cleanup * Delete threads from the instance table * Revert cargo file * Remove dead code * Clippy changes * Clippy * Revert unnecessary changes * Revert unnecessary changes * Revert unnecessary changes * Revert unnecessary changes * Revert unnecessary changes * Review comments * Review feedback * Make thread IDs per-component-instance * Fix config * Tighten up completion * Clippy * Trigger full PR test * Move funcref table reading * Remove unused import * Formatting * Rename RemoveOnDrop * Review feedback * Review feedback * Move start thread closure * Review feedback * Review feedback * Correct feature for import * Review feedback * Disable failing tests * Enable fixed tests * Review feedback * Readd tests * Ignore task deletion test with Miri
1 parent 1ec5a22 commit e06fbf7

30 files changed

Lines changed: 2470 additions & 371 deletions

File tree

crates/cli-flags/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ wasmtime_option_group! {
383383
/// Component model support for async lifting/lowering: this corresponds
384384
/// to the 🚟 emoji in the component model specification.
385385
pub component_model_async_stackful: Option<bool>,
386+
/// Component model support for threading: this corresponds
387+
/// to the 🧵 emoji in the component model specification.
388+
pub component_model_threading: Option<bool>,
386389
/// Component model support for `error-context`: this corresponds
387390
/// to the 📝 emoji in the component model specification.
388391
pub component_model_error_context: Option<bool>,
@@ -1059,6 +1062,7 @@ impl CommonOptions {
10591062
("component-model-async", component_model_async, wasm_component_model_async)
10601063
("component-model-async", component_model_async_builtins, wasm_component_model_async_builtins)
10611064
("component-model-async", component_model_async_stackful, wasm_component_model_async_stackful)
1065+
("component-model-async", component_model_threading, wasm_component_model_threading)
10621066
("component-model", component_model_error_context, wasm_component_model_error_context)
10631067
("threads", threads, wasm_threads)
10641068
("gc", gc, wasm_gc)

crates/cranelift/src/compiler/component.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,94 @@ impl<'a> TrampolineCompiler<'a> {
871871
},
872872
);
873873
}
874+
Trampoline::ThreadIndex => {
875+
self.translate_libcall(
876+
host::thread_index,
877+
TrapSentinel::NegativeOne,
878+
WasmArgs::InRegisters,
879+
|_, _| {},
880+
);
881+
}
882+
Trampoline::ThreadNewIndirect {
883+
instance,
884+
start_func_table_idx,
885+
start_func_ty_idx,
886+
} => {
887+
self.translate_libcall(
888+
host::thread_new_indirect,
889+
TrapSentinel::NegativeOne,
890+
WasmArgs::InRegisters,
891+
|me, params| {
892+
params.push(me.index_value(*instance));
893+
params.push(me.index_value(*start_func_table_idx));
894+
params.push(me.index_value(*start_func_ty_idx));
895+
},
896+
);
897+
}
898+
Trampoline::ThreadSwitchTo {
899+
instance,
900+
cancellable,
901+
} => {
902+
self.translate_libcall(
903+
host::thread_switch_to,
904+
TrapSentinel::NegativeOne,
905+
WasmArgs::InRegisters,
906+
|me, params| {
907+
params.push(me.index_value(*instance));
908+
params.push(
909+
me.builder
910+
.ins()
911+
.iconst(ir::types::I8, i64::from(*cancellable)),
912+
);
913+
},
914+
);
915+
}
916+
Trampoline::ThreadSuspend {
917+
instance,
918+
cancellable,
919+
} => {
920+
self.translate_libcall(
921+
host::thread_suspend,
922+
TrapSentinel::NegativeOne,
923+
WasmArgs::InRegisters,
924+
|me, params| {
925+
params.push(me.index_value(*instance));
926+
params.push(
927+
me.builder
928+
.ins()
929+
.iconst(ir::types::I8, i64::from(*cancellable)),
930+
);
931+
},
932+
);
933+
}
934+
Trampoline::ThreadResumeLater { instance } => {
935+
self.translate_libcall(
936+
host::thread_resume_later,
937+
TrapSentinel::Falsy,
938+
WasmArgs::InRegisters,
939+
|me, params| {
940+
params.push(me.index_value(*instance));
941+
},
942+
);
943+
}
944+
Trampoline::ThreadYieldTo {
945+
instance,
946+
cancellable,
947+
} => {
948+
self.translate_libcall(
949+
host::thread_yield_to,
950+
TrapSentinel::NegativeOne,
951+
WasmArgs::InRegisters,
952+
|me, params| {
953+
params.push(me.index_value(*instance));
954+
params.push(
955+
me.builder
956+
.ins()
957+
.iconst(ir::types::I8, i64::from(*cancellable)),
958+
);
959+
},
960+
);
961+
}
874962
}
875963
}
876964

crates/environ/src/component.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,18 @@ macro_rules! foreach_builtin_component_function {
189189
context_get(vmctx: vmctx, caller_instance: u32, slot: u32) -> u64;
190190
#[cfg(feature = "component-model-async")]
191191
context_set(vmctx: vmctx, caller_instance: u32, slot: u32, val: u32) -> bool;
192+
#[cfg(feature = "component-model-async")]
193+
thread_index(vmctx: vmctx) -> u64;
194+
#[cfg(feature = "component-model-async")]
195+
thread_new_indirect(vmctx: vmctx, caller_instance: u32, func_ty_id: u32, func_table_idx: u32, func_idx: u32, context: u32) -> u64;
196+
#[cfg(feature = "component-model-async")]
197+
thread_switch_to(vmctx: vmctx, caller_instance: u32, cancellable: u8, thread_idx: u32) -> u32;
198+
#[cfg(feature = "component-model-async")]
199+
thread_suspend(vmctx: vmctx, caller_instance: u32, cancellable: u8) -> u32;
200+
#[cfg(feature = "component-model-async")]
201+
thread_resume_later(vmctx: vmctx, caller_instance: u32, thread_idx: u32) -> bool;
202+
#[cfg(feature = "component-model-async")]
203+
thread_yield_to(vmctx: vmctx, caller_instance: u32, cancellable: u8, thread_idx: u32) -> u32;
192204

193205
trap(vmctx: vmctx, code: u8) -> bool;
194206

crates/environ/src/component/dfg.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,12 @@ pub struct ComponentDfg {
6969
/// Same as `reallocs`, but for post-return.
7070
pub post_returns: Intern<PostReturnId, CoreDef>,
7171

72-
/// Same as `reallocs`, but for post-return.
72+
/// Same as `reallocs`, but for memories.
7373
pub memories: Intern<MemoryId, CoreExport<MemoryIndex>>,
7474

75+
/// Same as `reallocs`, but for tables.
76+
pub tables: Intern<TableId, CoreExport<TableIndex>>,
77+
7578
/// Metadata about identified fused adapters.
7679
///
7780
/// Note that this list is required to be populated in-order where the
@@ -483,6 +486,27 @@ pub enum Trampoline {
483486
instance: RuntimeComponentInstanceIndex,
484487
slot: u32,
485488
},
489+
ThreadIndex,
490+
ThreadNewIndirect {
491+
instance: RuntimeComponentInstanceIndex,
492+
start_func_ty_idx: ComponentTypeIndex,
493+
start_func_table_id: TableId,
494+
},
495+
ThreadSwitchTo {
496+
instance: RuntimeComponentInstanceIndex,
497+
cancellable: bool,
498+
},
499+
ThreadSuspend {
500+
instance: RuntimeComponentInstanceIndex,
501+
cancellable: bool,
502+
},
503+
ThreadResumeLater {
504+
instance: RuntimeComponentInstanceIndex,
505+
},
506+
ThreadYieldTo {
507+
instance: RuntimeComponentInstanceIndex,
508+
cancellable: bool,
509+
},
486510
}
487511

488512
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
@@ -833,6 +857,15 @@ impl LinearizeDfg<'_> {
833857
)
834858
}
835859

860+
fn runtime_table(&mut self, table: TableId) -> RuntimeTableIndex {
861+
self.intern(
862+
table,
863+
|me| &mut me.runtime_tables,
864+
|me, table| me.core_export(&me.dfg.tables[table]),
865+
|index, export| GlobalInitializer::ExtractTable(ExtractTable { index, export }),
866+
)
867+
}
868+
836869
fn runtime_realloc(&mut self, realloc: ReallocId) -> RuntimeReallocIndex {
837870
self.intern(
838871
realloc,
@@ -1135,6 +1168,40 @@ impl LinearizeDfg<'_> {
11351168
instance: *instance,
11361169
slot: *slot,
11371170
},
1171+
Trampoline::ThreadIndex => info::Trampoline::ThreadIndex,
1172+
Trampoline::ThreadNewIndirect {
1173+
instance,
1174+
start_func_ty_idx,
1175+
start_func_table_id,
1176+
} => info::Trampoline::ThreadNewIndirect {
1177+
instance: *instance,
1178+
start_func_ty_idx: *start_func_ty_idx,
1179+
start_func_table_idx: self.runtime_table(*start_func_table_id),
1180+
},
1181+
Trampoline::ThreadSwitchTo {
1182+
instance,
1183+
cancellable,
1184+
} => info::Trampoline::ThreadSwitchTo {
1185+
instance: *instance,
1186+
cancellable: *cancellable,
1187+
},
1188+
Trampoline::ThreadSuspend {
1189+
instance,
1190+
cancellable,
1191+
} => info::Trampoline::ThreadSuspend {
1192+
instance: *instance,
1193+
cancellable: *cancellable,
1194+
},
1195+
Trampoline::ThreadResumeLater { instance } => info::Trampoline::ThreadResumeLater {
1196+
instance: *instance,
1197+
},
1198+
Trampoline::ThreadYieldTo {
1199+
instance,
1200+
cancellable,
1201+
} => info::Trampoline::ThreadYieldTo {
1202+
instance: *instance,
1203+
cancellable: *cancellable,
1204+
},
11381205
};
11391206
let i1 = self.trampolines.push(*signature);
11401207
let i2 = self.trampoline_defs.push(trampoline);

crates/environ/src/component/info.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,7 @@ pub enum Trampoline {
831831
ThreadYield {
832832
/// The specific component instance which is calling the intrinsic.
833833
instance: RuntimeComponentInstanceIndex,
834-
/// If `true`, indicates the caller instance maybe receive notification
834+
/// If `true`, indicates the caller instance may receive notification
835835
/// of task cancellation.
836836
cancellable: bool,
837837
},
@@ -1123,6 +1123,52 @@ pub enum Trampoline {
11231123
/// Which slot to update.
11241124
slot: u32,
11251125
},
1126+
1127+
/// Intrinsic used to implement the `thread.index` component model builtin.
1128+
ThreadIndex,
1129+
1130+
/// Intrinsic used to implement the `thread.new_indirect` component model builtin.
1131+
ThreadNewIndirect {
1132+
/// The specific component instance which is calling the intrinsic.
1133+
instance: RuntimeComponentInstanceIndex,
1134+
/// The type index for the start function of the thread.
1135+
start_func_ty_idx: ComponentTypeIndex,
1136+
/// The index of the table that stores the start function.
1137+
start_func_table_idx: RuntimeTableIndex,
1138+
},
1139+
1140+
/// Intrinsic used to implement the `thread.switch-to` component model builtin.
1141+
ThreadSwitchTo {
1142+
/// The specific component instance which is calling the intrinsic.
1143+
instance: RuntimeComponentInstanceIndex,
1144+
/// If `true`, indicates the caller instance may receive notification
1145+
/// of task cancellation.
1146+
cancellable: bool,
1147+
},
1148+
1149+
/// Intrinsic used to implement the `thread.suspend` component model builtin.
1150+
ThreadSuspend {
1151+
/// The specific component instance which is calling the intrinsic.
1152+
instance: RuntimeComponentInstanceIndex,
1153+
/// If `true`, indicates the caller instance may receive notification
1154+
/// of task cancellation.
1155+
cancellable: bool,
1156+
},
1157+
1158+
/// Intrinsic used to implement the `thread.resume-later` component model builtin.
1159+
ThreadResumeLater {
1160+
/// The specific component instance which is calling the intrinsic.
1161+
instance: RuntimeComponentInstanceIndex,
1162+
},
1163+
1164+
/// Intrinsic used to implement the `thread.yield-to` component model builtin.
1165+
ThreadYieldTo {
1166+
/// The specific component instance which is calling the intrinsic.
1167+
instance: RuntimeComponentInstanceIndex,
1168+
/// If `true`, indicates the caller instance may receive notification
1169+
/// of task cancellation.
1170+
cancellable: bool,
1171+
},
11261172
}
11271173

11281174
impl Trampoline {
@@ -1188,6 +1234,12 @@ impl Trampoline {
11881234
ErrorContextTransfer => format!("error-context-transfer"),
11891235
ContextGet { .. } => format!("context-get"),
11901236
ContextSet { .. } => format!("context-set"),
1237+
ThreadIndex => format!("thread-index"),
1238+
ThreadNewIndirect { .. } => format!("thread-new-indirect"),
1239+
ThreadSwitchTo { .. } => format!("thread-switch-to"),
1240+
ThreadSuspend { .. } => format!("thread-suspend"),
1241+
ThreadResumeLater { .. } => format!("thread-resume-later"),
1242+
ThreadYieldTo { .. } => format!("thread-yield-to"),
11911243
}
11921244
}
11931245
}

crates/environ/src/component/translate.rs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,29 @@ enum LocalInitializer<'data> {
318318
func: ModuleInternedTypeIndex,
319319
i: u32,
320320
},
321+
ThreadIndex {
322+
func: ModuleInternedTypeIndex,
323+
},
324+
ThreadNewIndirect {
325+
func: ModuleInternedTypeIndex,
326+
start_func_ty: ComponentTypeIndex,
327+
start_func_table_index: TableIndex,
328+
},
329+
ThreadSwitchTo {
330+
func: ModuleInternedTypeIndex,
331+
cancellable: bool,
332+
},
333+
ThreadSuspend {
334+
func: ModuleInternedTypeIndex,
335+
cancellable: bool,
336+
},
337+
ThreadResumeLater {
338+
func: ModuleInternedTypeIndex,
339+
},
340+
ThreadYieldTo {
341+
func: ModuleInternedTypeIndex,
342+
cancellable: bool,
343+
},
321344

322345
// core wasm modules
323346
ModuleStatic(StaticModuleIndex, ComponentCoreModuleTypeId),
@@ -1121,24 +1144,42 @@ impl<'a, 'data> Translator<'a, 'data> {
11211144
core_func_index += 1;
11221145
LocalInitializer::ContextSet { i, func }
11231146
}
1124-
11251147
wasmparser::CanonicalFunction::ThreadIndex => {
1126-
bail!("unimplemented `thread.index`");
1148+
let func = self.core_func_signature(core_func_index)?;
1149+
core_func_index += 1;
1150+
LocalInitializer::ThreadIndex { func }
11271151
}
1128-
wasmparser::CanonicalFunction::ThreadNewIndirect { .. } => {
1129-
bail!("unimplemented `thread.new-indirect`");
1152+
wasmparser::CanonicalFunction::ThreadNewIndirect {
1153+
func_ty_index,
1154+
table_index,
1155+
} => {
1156+
let func = self.core_func_signature(core_func_index)?;
1157+
core_func_index += 1;
1158+
LocalInitializer::ThreadNewIndirect {
1159+
func,
1160+
start_func_ty: ComponentTypeIndex::from_u32(func_ty_index),
1161+
start_func_table_index: TableIndex::from_u32(table_index),
1162+
}
11301163
}
1131-
wasmparser::CanonicalFunction::ThreadSwitchTo { .. } => {
1132-
bail!("unimplemented `thread.switch-to`");
1164+
wasmparser::CanonicalFunction::ThreadSwitchTo { cancellable } => {
1165+
let func = self.core_func_signature(core_func_index)?;
1166+
core_func_index += 1;
1167+
LocalInitializer::ThreadSwitchTo { func, cancellable }
11331168
}
1134-
wasmparser::CanonicalFunction::ThreadSuspend { .. } => {
1135-
bail!("unimplemented `thread.suspend`");
1169+
wasmparser::CanonicalFunction::ThreadSuspend { cancellable } => {
1170+
let func = self.core_func_signature(core_func_index)?;
1171+
core_func_index += 1;
1172+
LocalInitializer::ThreadSuspend { func, cancellable }
11361173
}
11371174
wasmparser::CanonicalFunction::ThreadResumeLater => {
1138-
bail!("unimplemented `thread.resume-later`");
1175+
let func = self.core_func_signature(core_func_index)?;
1176+
core_func_index += 1;
1177+
LocalInitializer::ThreadResumeLater { func }
11391178
}
1140-
wasmparser::CanonicalFunction::ThreadYieldTo { .. } => {
1141-
bail!("unimplemented `thread.yield-to`");
1179+
wasmparser::CanonicalFunction::ThreadYieldTo { cancellable } => {
1180+
let func = self.core_func_signature(core_func_index)?;
1181+
core_func_index += 1;
1182+
LocalInitializer::ThreadYieldTo { func, cancellable }
11421183
}
11431184
};
11441185
self.result.initializers.push(init);

0 commit comments

Comments
 (0)