Skip to content

Commit fd0a4f8

Browse files
authored
Implement co-op threading builtins (#2298)
* Implement co-op threading builtins * Change content.get/set validation * Change content.get/set validation * Improve context immediate validation * Update thread.new_indirect approach * Refactor import name validation and extraction * Split features
1 parent 544ac54 commit fd0a4f8

File tree

52 files changed

+2148
-446
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2148
-446
lines changed

crates/wasm-encoder/src/component/builder.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,9 @@ impl ComponentBuilder {
425425
inc(&mut self.core_funcs)
426426
}
427427

428-
/// Declares a new `task.yield` intrinsic.
429-
pub fn yield_(&mut self, async_: bool) -> u32 {
430-
self.canonical_functions().yield_(async_);
428+
/// Declares a new `thread.yield` intrinsic.
429+
pub fn thread_yield(&mut self, cancellable: bool) -> u32 {
430+
self.canonical_functions().thread_yield(cancellable);
431431
inc(&mut self.core_funcs)
432432
}
433433

@@ -577,14 +577,16 @@ impl ComponentBuilder {
577577
}
578578

579579
/// Declares a new `waitable-set.wait` intrinsic.
580-
pub fn waitable_set_wait(&mut self, async_: bool, memory: u32) -> u32 {
581-
self.canonical_functions().waitable_set_wait(async_, memory);
580+
pub fn waitable_set_wait(&mut self, cancellable: bool, memory: u32) -> u32 {
581+
self.canonical_functions()
582+
.waitable_set_wait(cancellable, memory);
582583
inc(&mut self.core_funcs)
583584
}
584585

585586
/// Declares a new `waitable-set.poll` intrinsic.
586-
pub fn waitable_set_poll(&mut self, async_: bool, memory: u32) -> u32 {
587-
self.canonical_functions().waitable_set_poll(async_, memory);
587+
pub fn waitable_set_poll(&mut self, cancellable: bool, memory: u32) -> u32 {
588+
self.canonical_functions()
589+
.waitable_set_poll(cancellable, memory);
588590
inc(&mut self.core_funcs)
589591
}
590592

@@ -600,6 +602,43 @@ impl ComponentBuilder {
600602
inc(&mut self.core_funcs)
601603
}
602604

605+
/// Declares a new `thread.index` intrinsic.
606+
pub fn thread_index(&mut self) -> u32 {
607+
self.canonical_functions().thread_index();
608+
inc(&mut self.core_funcs)
609+
}
610+
611+
/// Declares a new `thread.new_indirect` intrinsic.
612+
pub fn thread_new_indirect(&mut self, func_ty_idx: u32, table_index: u32) -> u32 {
613+
self.canonical_functions()
614+
.thread_new_indirect(func_ty_idx, table_index);
615+
inc(&mut self.core_funcs)
616+
}
617+
618+
/// Declares a new `thread.switch-to` intrinsic.
619+
pub fn thread_switch_to(&mut self, cancellable: bool) -> u32 {
620+
self.canonical_functions().thread_switch_to(cancellable);
621+
inc(&mut self.core_funcs)
622+
}
623+
624+
/// Declares a new `thread.suspend` intrinsic.
625+
pub fn thread_suspend(&mut self, cancellable: bool) -> u32 {
626+
self.canonical_functions().thread_suspend(cancellable);
627+
inc(&mut self.core_funcs)
628+
}
629+
630+
/// Declares a new `thread.resume-later` intrinsic.
631+
pub fn thread_resume_later(&mut self) -> u32 {
632+
self.canonical_functions().thread_resume_later();
633+
inc(&mut self.core_funcs)
634+
}
635+
636+
/// Declares a new `thread.yield-to` intrinsic.
637+
pub fn thread_yield_to(&mut self, cancellable: bool) -> u32 {
638+
self.canonical_functions().thread_yield_to(cancellable);
639+
inc(&mut self.core_funcs)
640+
}
641+
603642
/// Adds a new custom section to this component.
604643
pub fn custom_section(&mut self, section: &CustomSection<'_>) {
605644
self.flush();

crates/wasm-encoder/src/component/canonicals.rs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,10 @@ impl CanonicalFunctionSection {
249249
/// Defines a function which yields control to the host so that other tasks
250250
/// are able to make progress, if any.
251251
///
252-
/// If `async_` is true, the caller instance may be reentered.
253-
pub fn yield_(&mut self, async_: bool) -> &mut Self {
252+
/// If `cancellable` is true, the caller instance may be reentered.
253+
pub fn thread_yield(&mut self, cancellable: bool) -> &mut Self {
254254
self.bytes.push(0x0c);
255-
self.bytes.push(if async_ { 1 } else { 0 });
255+
self.bytes.push(if cancellable { 1 } else { 0 });
256256
self.num_added += 1;
257257
self
258258
}
@@ -498,6 +498,59 @@ impl CanonicalFunctionSection {
498498
self
499499
}
500500

501+
/// Declare a new `thread.index` intrinsic, used to get the index of the
502+
/// current thread.
503+
pub fn thread_index(&mut self) -> &mut Self {
504+
self.bytes.push(0x26);
505+
self.num_added += 1;
506+
self
507+
}
508+
509+
/// Declare a new `thread.new_indirect` intrinsic, used to create a new
510+
/// thread by invoking a function indirectly through a `funcref` table.
511+
pub fn thread_new_indirect(&mut self, ty_index: u32, table_index: u32) -> &mut Self {
512+
self.bytes.push(0x27);
513+
ty_index.encode(&mut self.bytes);
514+
table_index.encode(&mut self.bytes);
515+
self.num_added += 1;
516+
self
517+
}
518+
519+
/// Declare a new `thread.switch-to` intrinsic, used to switch execution to
520+
/// another thread.
521+
pub fn thread_switch_to(&mut self, cancellable: bool) -> &mut Self {
522+
self.bytes.push(0x28);
523+
self.bytes.push(if cancellable { 1 } else { 0 });
524+
self.num_added += 1;
525+
self
526+
}
527+
528+
/// Declare a new `thread.suspend` intrinsic, used to suspend execution of
529+
/// the current thread.
530+
pub fn thread_suspend(&mut self, cancellable: bool) -> &mut Self {
531+
self.bytes.push(0x29);
532+
self.bytes.push(if cancellable { 1 } else { 0 });
533+
self.num_added += 1;
534+
self
535+
}
536+
537+
/// Declare a new `thread.resume-later` intrinsic, used to resume execution
538+
/// of the given thread.
539+
pub fn thread_resume_later(&mut self) -> &mut Self {
540+
self.bytes.push(0x2a);
541+
self.num_added += 1;
542+
self
543+
}
544+
545+
/// Declare a new `thread.yield-to` intrinsic, used to yield execution to
546+
/// a given thread.
547+
pub fn thread_yield_to(&mut self, cancellable: bool) -> &mut Self {
548+
self.bytes.push(0x2b);
549+
self.bytes.push(if cancellable { 1 } else { 0 });
550+
self.num_added += 1;
551+
self
552+
}
553+
501554
fn encode_options<O>(&mut self, options: O) -> &mut Self
502555
where
503556
O: IntoIterator<Item = CanonicalOption>,

crates/wasm-encoder/src/reencode/component.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -995,8 +995,8 @@ pub mod component_utils {
995995
wasmparser::CanonicalFunction::ContextSet(i) => {
996996
section.context_set(i);
997997
}
998-
wasmparser::CanonicalFunction::Yield { async_ } => {
999-
section.yield_(async_);
998+
wasmparser::CanonicalFunction::ThreadYield { cancellable } => {
999+
section.thread_yield(cancellable);
10001000
}
10011001
wasmparser::CanonicalFunction::SubtaskDrop => {
10021002
section.subtask_drop();
@@ -1082,18 +1082,47 @@ pub mod component_utils {
10821082
wasmparser::CanonicalFunction::WaitableSetNew => {
10831083
section.waitable_set_new();
10841084
}
1085-
wasmparser::CanonicalFunction::WaitableSetWait { async_, memory } => {
1086-
section.waitable_set_wait(async_, reencoder.memory_index(memory)?);
1085+
wasmparser::CanonicalFunction::WaitableSetWait {
1086+
cancellable,
1087+
memory,
1088+
} => {
1089+
section.waitable_set_wait(cancellable, reencoder.memory_index(memory)?);
10871090
}
1088-
wasmparser::CanonicalFunction::WaitableSetPoll { async_, memory } => {
1089-
section.waitable_set_poll(async_, reencoder.memory_index(memory)?);
1091+
wasmparser::CanonicalFunction::WaitableSetPoll {
1092+
cancellable,
1093+
memory,
1094+
} => {
1095+
section.waitable_set_poll(cancellable, reencoder.memory_index(memory)?);
10901096
}
10911097
wasmparser::CanonicalFunction::WaitableSetDrop => {
10921098
section.waitable_set_drop();
10931099
}
10941100
wasmparser::CanonicalFunction::WaitableJoin => {
10951101
section.waitable_join();
10961102
}
1103+
wasmparser::CanonicalFunction::ThreadIndex => {
1104+
section.thread_index();
1105+
}
1106+
wasmparser::CanonicalFunction::ThreadNewIndirect {
1107+
func_ty_index,
1108+
table_index,
1109+
} => {
1110+
let func_ty = reencoder.type_index(func_ty_index)?;
1111+
let table_index = reencoder.table_index(table_index)?;
1112+
section.thread_new_indirect(func_ty, table_index);
1113+
}
1114+
wasmparser::CanonicalFunction::ThreadSwitchTo { cancellable } => {
1115+
section.thread_switch_to(cancellable);
1116+
}
1117+
wasmparser::CanonicalFunction::ThreadSuspend { cancellable } => {
1118+
section.thread_suspend(cancellable);
1119+
}
1120+
wasmparser::CanonicalFunction::ThreadResumeLater => {
1121+
section.thread_resume_later();
1122+
}
1123+
wasmparser::CanonicalFunction::ThreadYieldTo { cancellable } => {
1124+
section.thread_yield_to(cancellable);
1125+
}
10971126
}
10981127
Ok(())
10991128
}

crates/wasmparser/src/features.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,36 +265,41 @@ define_wasm_features! {
265265
/// Corresponds to the 🚝 character in
266266
/// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
267267
pub cm_async_builtins: CM_ASYNC_BUILTINS(1 << 29) = false;
268+
/// Support for threading in the component model proposal.
269+
///
270+
/// Corresponds to the 🧵 character in
271+
/// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
272+
pub cm_threading: CM_THREADING(1 << 30) = false;
268273
/// Gates some intrinsics being marked with `error-context` in the component
269274
/// model async proposal.
270275
///
271276
/// Corresponds to the 📝 character in
272277
/// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
273-
pub cm_error_context: CM_ERROR_CONTEXT(1 << 30) = false;
278+
pub cm_error_context: CM_ERROR_CONTEXT(1 << 31) = false;
274279
/// Support for fixed size lists
275280
///
276281
/// Corresponds to the 🔧 character in
277282
/// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
278-
pub cm_fixed_size_list: CM_FIXED_SIZE_LIST(1 << 31) = false;
283+
pub cm_fixed_size_list: CM_FIXED_SIZE_LIST(1 << 32) = false;
279284
/// Support for Wasm GC in the component model proposal.
280285
///
281286
/// Corresponds to the 🛸 character in
282287
/// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
283-
pub cm_gc: CM_GC(1 << 32) = false;
288+
pub cm_gc: CM_GC(1 << 33) = false;
284289

285290
/// Subset of the reference-types WebAssembly proposal which only
286291
/// encompasses the leb-encoding of the table immediate to the
287292
/// `call_indirect` instruction, enabling over-long encodings of an
288293
/// integer for example.
289294
///
290295
/// This is a subcomponent of the "lime1" feature.
291-
pub call_indirect_overlong: CALL_INDIRECT_OVERLONG(1 << 33) = true;
296+
pub call_indirect_overlong: CALL_INDIRECT_OVERLONG(1 << 34) = true;
292297

293298
/// Subset of the bulk-memory proposal covering just the `memory.copy`
294299
/// and `memory.fill` instructions.
295300
///
296301
/// This is a subcomponent of the "lime1" feature.
297-
pub bulk_memory_opt: BULK_MEMORY_OPT(1 << 34) = true;
302+
pub bulk_memory_opt: BULK_MEMORY_OPT(1 << 35) = true;
298303
}
299304
}
300305

crates/wasmparser/src/readers/component/canonicals.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ pub enum CanonicalFunction {
110110
ContextSet(u32),
111111
/// A function which yields control to the host so that other tasks are able
112112
/// to make progress, if any.
113-
Yield {
113+
ThreadYield {
114114
/// If `true`, indicates the caller instance maybe reentered.
115-
async_: bool,
115+
cancellable: bool,
116116
},
117117
/// A function to drop a specified task which has completed.
118118
SubtaskDrop,
@@ -246,22 +246,48 @@ pub enum CanonicalFunction {
246246
WaitableSetWait {
247247
/// Whether or not the guest can be reentered while calling this
248248
/// function.
249-
async_: bool,
249+
cancellable: bool,
250250
/// Which memory the results of this operation are stored in.
251251
memory: u32,
252252
},
253253
/// A function to check if any items are ready within a `waitable-set`.
254254
WaitableSetPoll {
255255
/// Whether or not the guest can be reentered while calling this
256256
/// function.
257-
async_: bool,
257+
cancellable: bool,
258258
/// Which memory the results of this operation are stored in.
259259
memory: u32,
260260
},
261261
/// A function to drop a `waitable-set`.
262262
WaitableSetDrop,
263263
/// A function to add an item to a `waitable-set`.
264264
WaitableJoin,
265+
/// A function to get the index of the current thread.
266+
ThreadIndex,
267+
/// A function to create a new thread with the specified start function.
268+
ThreadNewIndirect {
269+
/// The index of the function type to use as the start function.
270+
func_ty_index: u32,
271+
/// The index of the table to use.
272+
table_index: u32,
273+
},
274+
/// A function to suspend the current thread and switch to the given thread.
275+
ThreadSwitchTo {
276+
/// Whether or not the thread can be cancelled while awaiting resumption.
277+
cancellable: bool,
278+
},
279+
/// A function to suspend the current thread, immediately yielding to any transitive async-lowered calling component.
280+
ThreadSuspend {
281+
/// Whether or not the thread can be cancelled while suspended.
282+
cancellable: bool,
283+
},
284+
/// A function to schedule the given thread to be resumed later.
285+
ThreadResumeLater,
286+
/// A function to suspend the current thread and switch to the given thread.
287+
ThreadYieldTo {
288+
/// Whether or not the thread can be cancelled while yielding.
289+
cancellable: bool,
290+
},
265291
}
266292

267293
/// A reader for the canonical section of a WebAssembly component.
@@ -310,8 +336,8 @@ impl<'a> FromReader<'a> for CanonicalFunction {
310336
0x7f => CanonicalFunction::ContextSet(reader.read_var_u32()?),
311337
x => return reader.invalid_leading_byte(x, "context.set intrinsic type"),
312338
},
313-
0x0c => CanonicalFunction::Yield {
314-
async_: reader.read()?,
339+
0x0c => CanonicalFunction::ThreadYield {
340+
cancellable: reader.read()?,
315341
},
316342
0x0d => CanonicalFunction::SubtaskDrop,
317343
0x0e => CanonicalFunction::StreamNew { ty: reader.read()? },
@@ -362,15 +388,30 @@ impl<'a> FromReader<'a> for CanonicalFunction {
362388

363389
0x1f => CanonicalFunction::WaitableSetNew,
364390
0x20 => CanonicalFunction::WaitableSetWait {
365-
async_: reader.read()?,
391+
cancellable: reader.read()?,
366392
memory: reader.read()?,
367393
},
368394
0x21 => CanonicalFunction::WaitableSetPoll {
369-
async_: reader.read()?,
395+
cancellable: reader.read()?,
370396
memory: reader.read()?,
371397
},
372398
0x22 => CanonicalFunction::WaitableSetDrop,
373399
0x23 => CanonicalFunction::WaitableJoin,
400+
0x26 => CanonicalFunction::ThreadIndex,
401+
0x27 => CanonicalFunction::ThreadNewIndirect {
402+
func_ty_index: reader.read()?,
403+
table_index: reader.read()?,
404+
},
405+
0x28 => CanonicalFunction::ThreadSwitchTo {
406+
cancellable: reader.read()?,
407+
},
408+
0x29 => CanonicalFunction::ThreadSuspend {
409+
cancellable: reader.read()?,
410+
},
411+
0x2a => CanonicalFunction::ThreadResumeLater,
412+
0x2b => CanonicalFunction::ThreadYieldTo {
413+
cancellable: reader.read()?,
414+
},
374415
0x06 => CanonicalFunction::SubtaskCancel {
375416
async_: reader.read()?,
376417
},

0 commit comments

Comments
 (0)