Skip to content

Commit

Permalink
Implement waitable-sets (bytecodealliance#2068)
Browse files Browse the repository at this point in the history
This commit catches up with WebAssembly/component-model#438 with respect
to the addition of `waitable-set` pseudo-resources and new intrinsics.
The old `task.wait` and `task.poll` intrinsics are renamed/repurposed
under these new names along with some other management intrinsics for
`waitable-set`s.
  • Loading branch information
alexcrichton authored Feb 24, 2025
1 parent a4b41c6 commit 1dae3da
Show file tree
Hide file tree
Showing 28 changed files with 979 additions and 578 deletions.
42 changes: 30 additions & 12 deletions crates/wasm-encoder/src/component/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,18 +407,6 @@ impl ComponentBuilder {
inc(&mut self.core_funcs)
}

/// Declares a new `task.wait` intrinsic.
pub fn task_wait(&mut self, async_: bool, memory: u32) -> u32 {
self.canonical_functions().task_wait(async_, memory);
inc(&mut self.core_funcs)
}

/// Declares a new `task.poll` intrinsic.
pub fn task_poll(&mut self, async_: bool, memory: u32) -> u32 {
self.canonical_functions().task_poll(async_, memory);
inc(&mut self.core_funcs)
}

/// Declares a new `task.yield` intrinsic.
pub fn task_yield(&mut self, async_: bool) -> u32 {
self.canonical_functions().task_yield(async_);
Expand Down Expand Up @@ -558,6 +546,36 @@ impl ComponentBuilder {
inc(&mut self.core_funcs)
}

/// Declares a new `waitable-set.new` intrinsic.
pub fn waitable_set_new(&mut self) -> u32 {
self.canonical_functions().waitable_set_new();
inc(&mut self.core_funcs)
}

/// Declares a new `waitable-set.wait` intrinsic.
pub fn waitable_set_wait(&mut self, async_: bool, memory: u32) -> u32 {
self.canonical_functions().waitable_set_wait(async_, memory);
inc(&mut self.core_funcs)
}

/// Declares a new `waitable-set.poll` intrinsic.
pub fn waitable_set_poll(&mut self, async_: bool, memory: u32) -> u32 {
self.canonical_functions().waitable_set_poll(async_, memory);
inc(&mut self.core_funcs)
}

/// Declares a new `waitable-set.drop` intrinsic.
pub fn waitable_set_drop(&mut self) -> u32 {
self.canonical_functions().waitable_set_drop();
inc(&mut self.core_funcs)
}

/// Declares a new `waitable.join` intrinsic.
pub fn waitable_join(&mut self) -> u32 {
self.canonical_functions().waitable_join();
inc(&mut self.core_funcs)
}

/// Adds a new custom section to this component.
pub fn custom_section(&mut self, section: &CustomSection<'_>) {
self.flush();
Expand Down
69 changes: 44 additions & 25 deletions crates/wasm-encoder/src/component/canonicals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,31 +200,6 @@ impl CanonicalFunctionSection {
self
}

/// Defines a function which waits for at least one outstanding async
/// task/stream/future to make progress, returning the first such event.
///
/// If `async_` is true, the caller instance may be reentered.
pub fn task_wait(&mut self, async_: bool, memory: u32) -> &mut Self {
self.bytes.push(0x0a);
self.bytes.push(if async_ { 1 } else { 0 });
memory.encode(&mut self.bytes);
self.num_added += 1;
self
}

/// Defines a function which checks whether any outstanding async
/// task/stream/future has made progress. Unlike `task.wait`, this does not
/// block and may return nothing if no such event has occurred.
///
/// If `async_` is true, the caller instance may be reentered.
pub fn task_poll(&mut self, async_: bool, memory: u32) -> &mut Self {
self.bytes.push(0x0b);
self.bytes.push(if async_ { 1 } else { 0 });
memory.encode(&mut self.bytes);
self.num_added += 1;
self
}

/// Defines a function which yields control to the host so that other tasks
/// are able to make progress, if any.
///
Expand Down Expand Up @@ -425,6 +400,50 @@ impl CanonicalFunctionSection {
self
}

/// Declare a new `waitable-set.new` intrinsic, used to create a
/// `waitable-set` pseudo-resource.
pub fn waitable_set_new(&mut self) -> &mut Self {
self.bytes.push(0x1f);
self.num_added += 1;
self
}

/// Declare a new `waitable-set.wait` intrinsic, used to block on a
/// `waitable-set`.
pub fn waitable_set_wait(&mut self, async_: bool, memory: u32) -> &mut Self {
self.bytes.push(0x20);
self.bytes.push(if async_ { 1 } else { 0 });
memory.encode(&mut self.bytes);
self.num_added += 1;
self
}

/// Declare a new `waitable-set.wait` intrinsic, used to check, without
/// blocking, if anything in a `waitable-set` is ready.
pub fn waitable_set_poll(&mut self, async_: bool, memory: u32) -> &mut Self {
self.bytes.push(0x21);
self.bytes.push(if async_ { 1 } else { 0 });
memory.encode(&mut self.bytes);
self.num_added += 1;
self
}

/// Declare a new `waitable-set.drop` intrinsic, used to dispose a
/// `waitable-set` pseudo-resource.
pub fn waitable_set_drop(&mut self) -> &mut Self {
self.bytes.push(0x22);
self.num_added += 1;
self
}

/// Declare a new `waitable.join` intrinsic, used to add an item to a
/// `waitable-set`.
pub fn waitable_join(&mut self) -> &mut Self {
self.bytes.push(0x23);
self.num_added += 1;
self
}

fn encode_options<O>(&mut self, options: O) -> &mut Self
where
O: IntoIterator<Item = CanonicalOption>,
Expand Down
21 changes: 15 additions & 6 deletions crates/wasm-encoder/src/reencode/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -969,12 +969,6 @@ pub mod component_utils {
options.iter().map(|o| reencoder.canonical_option(*o)),
);
}
wasmparser::CanonicalFunction::TaskWait { async_, memory } => {
section.task_wait(async_, reencoder.memory_index(memory));
}
wasmparser::CanonicalFunction::TaskPoll { async_, memory } => {
section.task_poll(async_, reencoder.memory_index(memory));
}
wasmparser::CanonicalFunction::TaskYield { async_ } => {
section.task_yield(async_);
}
Expand Down Expand Up @@ -1046,6 +1040,21 @@ pub mod component_utils {
wasmparser::CanonicalFunction::ErrorContextDrop => {
section.error_context_drop();
}
wasmparser::CanonicalFunction::WaitableSetNew => {
section.waitable_set_new();
}
wasmparser::CanonicalFunction::WaitableSetWait { async_, memory } => {
section.waitable_set_wait(async_, reencoder.memory_index(memory));
}
wasmparser::CanonicalFunction::WaitableSetPoll { async_, memory } => {
section.waitable_set_poll(async_, reencoder.memory_index(memory));
}
wasmparser::CanonicalFunction::WaitableSetDrop => {
section.waitable_set_drop();
}
wasmparser::CanonicalFunction::WaitableJoin => {
section.waitable_join();
}
}
Ok(())
}
Expand Down
59 changes: 34 additions & 25 deletions crates/wasmparser/src/readers/component/canonicals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,23 +90,6 @@ pub enum CanonicalFunction {
/// The canonical options for the function.
options: Box<[CanonicalOption]>,
},
/// A function which waits for at least one outstanding async
/// task/stream/future to make progress, returning the first such event.
TaskWait {
/// If `true`, indicates the caller instance maybe reentered.
async_: bool,
/// Memory to use when storing the event.
memory: u32,
},
/// A function which checks whether any outstanding async task/stream/future
/// has made progress. Unlike `task.wait`, this does not block and may
/// return nothing if no such event has occurred.
TaskPoll {
/// If `true`, indicates the caller instance maybe reentered.
async_: bool,
/// Memory to use when storing the event, if any.
memory: u32,
},
/// A function which yields control to the host so that other tasks are able
/// to make progress, if any.
TaskYield {
Expand Down Expand Up @@ -233,6 +216,28 @@ pub enum CanonicalFunction {
},
/// A function to drop a specified `error-context`.
ErrorContextDrop,
/// A function to create a new `waitable-set`.
WaitableSetNew,
/// A function to block on the next item within a `waitable-set`.
WaitableSetWait {
/// Whether or not the guest can be reentered while calling this
/// function.
async_: bool,
/// Which memory the results of this operation are stored in.
memory: u32,
},
/// A function to check if any items are ready within a `waitable-set`.
WaitableSetPoll {
/// Whether or not the guest can be reentered while calling this
/// function.
async_: bool,
/// Which memory the results of this operation are stored in.
memory: u32,
},
/// A function to drop a `waitable-set`.
WaitableSetDrop,
/// A function to add an item to a `waitable-set`.
WaitableJoin,
}

/// A reader for the canonical section of a WebAssembly component.
Expand Down Expand Up @@ -277,14 +282,6 @@ impl<'a> FromReader<'a> for CanonicalFunction {
result: crate::read_resultlist(reader)?,
options: read_opts(reader)?,
},
0x0a => CanonicalFunction::TaskWait {
async_: reader.read()?,
memory: reader.read()?,
},
0x0b => CanonicalFunction::TaskPoll {
async_: reader.read()?,
memory: reader.read()?,
},
0x0c => CanonicalFunction::TaskYield {
async_: reader.read()?,
},
Expand Down Expand Up @@ -334,6 +331,18 @@ impl<'a> FromReader<'a> for CanonicalFunction {
options: read_opts(reader)?,
},
0x1e => CanonicalFunction::ErrorContextDrop,

0x1f => CanonicalFunction::WaitableSetNew,
0x20 => CanonicalFunction::WaitableSetWait {
async_: reader.read()?,
memory: reader.read()?,
},
0x21 => CanonicalFunction::WaitableSetPoll {
async_: reader.read()?,
memory: reader.read()?,
},
0x22 => CanonicalFunction::WaitableSetDrop,
0x23 => CanonicalFunction::WaitableJoin,
x => return reader.invalid_leading_byte(x, "canonical function"),
})
}
Expand Down
105 changes: 1 addition & 104 deletions crates/wasmparser/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1272,110 +1272,7 @@ impl Validator {
},
|components, types, features, func, offset| {
let current = components.last_mut().unwrap();
match func {
crate::CanonicalFunction::Lift {
core_func_index,
type_index,
options,
} => current.lift_function(
core_func_index,
type_index,
&options,
types,
offset,
features,
),
crate::CanonicalFunction::Lower {
func_index,
options,
} => current.lower_function(func_index, &options, types, offset, features),
crate::CanonicalFunction::ResourceNew { resource } => {
current.resource_new(resource, types, offset)
}
crate::CanonicalFunction::ResourceDrop { resource } => {
current.resource_drop(resource, types, offset)
}
crate::CanonicalFunction::ResourceDropAsync { resource } => {
current.resource_drop_async(resource, types, offset, features)
}
crate::CanonicalFunction::ResourceRep { resource } => {
current.resource_rep(resource, types, offset)
}
crate::CanonicalFunction::ThreadSpawn { func_ty_index } => {
current.thread_spawn(func_ty_index, types, offset, features)
}
crate::CanonicalFunction::ThreadAvailableParallelism => {
current.thread_available_parallelism(types, offset, features)
}
crate::CanonicalFunction::BackpressureSet => {
current.backpressure_set(types, offset, features)
}
crate::CanonicalFunction::TaskReturn { result, options } => {
current.task_return(&result, &options, types, offset, features)
}
crate::CanonicalFunction::TaskWait { async_, memory } => {
current.task_wait(async_, memory, types, offset, features)
}
crate::CanonicalFunction::TaskPoll { async_, memory } => {
current.task_poll(async_, memory, types, offset, features)
}
crate::CanonicalFunction::TaskYield { async_ } => {
current.task_yield(async_, types, offset, features)
}
crate::CanonicalFunction::SubtaskDrop => {
current.subtask_drop(types, offset, features)
}
crate::CanonicalFunction::StreamNew { ty } => {
current.stream_new(ty, types, offset, features)
}
crate::CanonicalFunction::StreamRead { ty, options } => {
current.stream_read(ty, &options, types, offset, features)
}
crate::CanonicalFunction::StreamWrite { ty, options } => {
current.stream_write(ty, &options, types, offset, features)
}
crate::CanonicalFunction::StreamCancelRead { ty, async_ } => {
current.stream_cancel_read(ty, async_, types, offset, features)
}
crate::CanonicalFunction::StreamCancelWrite { ty, async_ } => {
current.stream_cancel_write(ty, async_, types, offset, features)
}
crate::CanonicalFunction::StreamCloseReadable { ty } => {
current.stream_close_readable(ty, types, offset, features)
}
crate::CanonicalFunction::StreamCloseWritable { ty } => {
current.stream_close_writable(ty, types, offset, features)
}
crate::CanonicalFunction::FutureNew { ty } => {
current.future_new(ty, types, offset, features)
}
crate::CanonicalFunction::FutureRead { ty, options } => {
current.future_read(ty, &options, types, offset, features)
}
crate::CanonicalFunction::FutureWrite { ty, options } => {
current.future_write(ty, options.into_vec(), types, offset, features)
}
crate::CanonicalFunction::FutureCancelRead { ty, async_ } => {
current.future_cancel_read(ty, async_, types, offset, features)
}
crate::CanonicalFunction::FutureCancelWrite { ty, async_ } => {
current.future_cancel_write(ty, async_, types, offset, features)
}
crate::CanonicalFunction::FutureCloseReadable { ty } => {
current.future_close_readable(ty, types, offset, features)
}
crate::CanonicalFunction::FutureCloseWritable { ty } => {
current.future_close_writable(ty, types, offset, features)
}
crate::CanonicalFunction::ErrorContextNew { options } => {
current.error_context_new(options.into_vec(), types, offset, features)
}
crate::CanonicalFunction::ErrorContextDebugMessage { options } => current
.error_context_debug_message(options.into_vec(), types, offset, features),
crate::CanonicalFunction::ErrorContextDrop => {
current.error_context_drop(types, offset, features)
}
}
current.canonical_function(func, types, offset, features)
},
)
}
Expand Down
Loading

0 comments on commit 1dae3da

Please sign in to comment.