Repository: bytecodealliance/wasm-tools
Pull request #2068 (2025-02-24 20:10:50)

Implement waitable-sets
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.


Patch:
diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs
index 567d1db7..e95dd060 100644
--- a/crates/wasm-encoder/src/component/builder.rs
+++ b/crates/wasm-encoder/src/component/builder.rs
@@ -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_);
@@ -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();
diff --git a/crates/wasm-encoder/src/component/canonicals.rs b/crates/wasm-encoder/src/component/canonicals.rs
index 7f36a671..4a757199 100644
--- a/crates/wasm-encoder/src/component/canonicals.rs
+++ b/crates/wasm-encoder/src/component/canonicals.rs
@@ -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.
     ///
@@ -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>,
diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs
index 3eb436dd..fa34b5e4 100644
--- a/crates/wasm-encoder/src/reencode/component.rs
+++ b/crates/wasm-encoder/src/reencode/component.rs
@@ -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_);
             }
@@ -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(())
     }
diff --git a/crates/wasmparser/src/readers/component/canonicals.rs b/crates/wasmparser/src/readers/component/canonicals.rs
index b907bf66..47324df6 100644
--- a/crates/wasmparser/src/readers/component/canonicals.rs
+++ b/crates/wasmparser/src/readers/component/canonicals.rs
@@ -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 {
@@ -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.
@@ -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()?,
             },
@@ -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"),
         })
     }
diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs
index 6f1c3358..e61095a7 100644
--- a/crates/wasmparser/src/validator.rs
+++ b/crates/wasmparser/src/validator.rs
@@ -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)
             },
         )
     }
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index 461878d9..d6577352 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -18,10 +18,10 @@ use crate::limits::*;
 use crate::prelude::*;
 use crate::validator::names::{ComponentName, ComponentNameKind, KebabStr, KebabString};
 use crate::{
-    BinaryReaderError, CanonicalOption, ComponentExportName, ComponentExternalKind,
-    ComponentOuterAliasKind, ComponentTypeRef, CompositeInnerType, ExternalKind, FuncType,
-    GlobalType, InstantiationArgKind, MemoryType, PackedIndex, RefType, Result, SubType, TableType,
-    TypeBounds, ValType, WasmFeatures,
+    BinaryReaderError, CanonicalFunction, CanonicalOption, ComponentExportName,
+    ComponentExternalKind, ComponentOuterAliasKind, ComponentTypeRef, CompositeInnerType,
+    ExternalKind, FuncType, GlobalType, InstantiationArgKind, MemoryType, PackedIndex, RefType,
+    Result, SubType, TableType, TypeBounds, ValType, WasmFeatures,
 };
 use core::mem;
 
@@ -951,7 +951,114 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn lift_function(
+    pub fn canonical_function(
+        &mut self,
+        func: CanonicalFunction,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        match func {
+            CanonicalFunction::Lift {
+                core_func_index,
+                type_index,
+                options,
+            } => self.lift_function(
+                core_func_index,
+                type_index,
+                &options,
+                types,
+                offset,
+                features,
+            ),
+            CanonicalFunction::Lower {
+                func_index,
+                options,
+            } => self.lower_function(func_index, &options, types, offset, features),
+            CanonicalFunction::ResourceNew { resource } => {
+                self.resource_new(resource, types, offset)
+            }
+            CanonicalFunction::ResourceDrop { resource } => {
+                self.resource_drop(resource, types, offset)
+            }
+            CanonicalFunction::ResourceDropAsync { resource } => {
+                self.resource_drop_async(resource, types, offset, features)
+            }
+            CanonicalFunction::ResourceRep { resource } => {
+                self.resource_rep(resource, types, offset)
+            }
+            CanonicalFunction::ThreadSpawn { func_ty_index } => {
+                self.thread_spawn(func_ty_index, types, offset, features)
+            }
+            CanonicalFunction::ThreadAvailableParallelism => {
+                self.thread_available_parallelism(types, offset, features)
+            }
+            CanonicalFunction::BackpressureSet => self.backpressure_set(types, offset, features),
+            CanonicalFunction::TaskReturn { result, options } => {
+                self.task_return(&result, &options, types, offset, features)
+            }
+            CanonicalFunction::TaskYield { async_ } => {
+                self.task_yield(async_, types, offset, features)
+            }
+            CanonicalFunction::SubtaskDrop => self.subtask_drop(types, offset, features),
+            CanonicalFunction::StreamNew { ty } => self.stream_new(ty, types, offset, features),
+            CanonicalFunction::StreamRead { ty, options } => {
+                self.stream_read(ty, &options, types, offset, features)
+            }
+            CanonicalFunction::StreamWrite { ty, options } => {
+                self.stream_write(ty, &options, types, offset, features)
+            }
+            CanonicalFunction::StreamCancelRead { ty, async_ } => {
+                self.stream_cancel_read(ty, async_, types, offset, features)
+            }
+            CanonicalFunction::StreamCancelWrite { ty, async_ } => {
+                self.stream_cancel_write(ty, async_, types, offset, features)
+            }
+            CanonicalFunction::StreamCloseReadable { ty } => {
+                self.stream_close_readable(ty, types, offset, features)
+            }
+            CanonicalFunction::StreamCloseWritable { ty } => {
+                self.stream_close_writable(ty, types, offset, features)
+            }
+            CanonicalFunction::FutureNew { ty } => self.future_new(ty, types, offset, features),
+            CanonicalFunction::FutureRead { ty, options } => {
+                self.future_read(ty, &options, types, offset, features)
+            }
+            CanonicalFunction::FutureWrite { ty, options } => {
+                self.future_write(ty, options.into_vec(), types, offset, features)
+            }
+            CanonicalFunction::FutureCancelRead { ty, async_ } => {
+                self.future_cancel_read(ty, async_, types, offset, features)
+            }
+            CanonicalFunction::FutureCancelWrite { ty, async_ } => {
+                self.future_cancel_write(ty, async_, types, offset, features)
+            }
+            CanonicalFunction::FutureCloseReadable { ty } => {
+                self.future_close_readable(ty, types, offset, features)
+            }
+            CanonicalFunction::FutureCloseWritable { ty } => {
+                self.future_close_writable(ty, types, offset, features)
+            }
+            CanonicalFunction::ErrorContextNew { options } => {
+                self.error_context_new(options.into_vec(), types, offset, features)
+            }
+            CanonicalFunction::ErrorContextDebugMessage { options } => {
+                self.error_context_debug_message(options.into_vec(), types, offset, features)
+            }
+            CanonicalFunction::ErrorContextDrop => self.error_context_drop(types, offset, features),
+            CanonicalFunction::WaitableSetNew => self.waitable_set_new(types, offset, features),
+            CanonicalFunction::WaitableSetWait { async_, memory } => {
+                self.waitable_set_wait(async_, memory, types, offset, features)
+            }
+            CanonicalFunction::WaitableSetPoll { async_, memory } => {
+                self.waitable_set_poll(async_, memory, types, offset, features)
+            }
+            CanonicalFunction::WaitableSetDrop => self.waitable_set_drop(types, offset, features),
+            CanonicalFunction::WaitableJoin => self.waitable_join(types, offset, features),
+        }
+    }
+
+    fn lift_function(
         &mut self,
         core_func_index: u32,
         type_index: u32,
@@ -994,7 +1101,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn lower_function(
+    fn lower_function(
         &mut self,
         func_index: u32,
         options: &[CanonicalOption],
@@ -1023,31 +1130,21 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn resource_new(
-        &mut self,
-        resource: u32,
-        types: &mut TypeAlloc,
-        offset: usize,
-    ) -> Result<()> {
+    fn resource_new(&mut self, resource: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> {
         let rep = self.check_local_resource(resource, types, offset)?;
         let id = types.intern_func_type(FuncType::new([rep], [ValType::I32]), offset);
         self.core_funcs.push(id);
         Ok(())
     }
 
-    pub fn resource_drop(
-        &mut self,
-        resource: u32,
-        types: &mut TypeAlloc,
-        offset: usize,
-    ) -> Result<()> {
+    fn resource_drop(&mut self, resource: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> {
         self.resource_at(resource, types, offset)?;
         let id = types.intern_func_type(FuncType::new([ValType::I32], []), offset);
         self.core_funcs.push(id);
         Ok(())
     }
 
-    pub fn resource_drop_async(
+    fn resource_drop_async(
         &mut self,
         resource: u32,
         types: &mut TypeAlloc,
@@ -1066,19 +1163,14 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn resource_rep(
-        &mut self,
-        resource: u32,
-        types: &mut TypeAlloc,
-        offset: usize,
-    ) -> Result<()> {
+    fn resource_rep(&mut self, resource: u32, types: &mut TypeAlloc, offset: usize) -> Result<()> {
         let rep = self.check_local_resource(resource, types, offset)?;
         let id = types.intern_func_type(FuncType::new([ValType::I32], [rep]), offset);
         self.core_funcs.push(id);
         Ok(())
     }
 
-    pub fn backpressure_set(
+    fn backpressure_set(
         &mut self,
         types: &mut TypeAlloc,
         offset: usize,
@@ -1096,7 +1188,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn task_return(
+    fn task_return(
         &mut self,
         result: &Option<crate::ComponentValType>,
         options: &[CanonicalOption],
@@ -1157,51 +1249,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn task_wait(
-        &mut self,
-        _async_: bool,
-        memory: u32,
-        types: &mut TypeAlloc,
-        offset: usize,
-        features: &WasmFeatures,
-    ) -> Result<()> {
-        if !features.component_model_async() {
-            bail!(
-                offset,
-                "`task.wait` requires the component model async feature"
-            )
-        }
-
-        self.memory_at(memory, offset)?;
-
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset));
-        Ok(())
-    }
-
-    pub fn task_poll(
-        &mut self,
-        _async_: bool,
-        memory: u32,
-        types: &mut TypeAlloc,
-        offset: usize,
-        features: &WasmFeatures,
-    ) -> Result<()> {
-        if !features.component_model_async() {
-            bail!(
-                offset,
-                "`task.poll` requires the component model async feature"
-            )
-        }
-
-        self.memory_at(memory, offset)?;
-
-        self.core_funcs
-            .push(types.intern_func_type(FuncType::new([ValType::I32], [ValType::I32]), offset));
-        Ok(())
-    }
-
-    pub fn task_yield(
+    fn task_yield(
         &mut self,
         _async_: bool,
         types: &mut TypeAlloc,
@@ -1220,7 +1268,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn subtask_drop(
+    fn subtask_drop(
         &mut self,
         types: &mut TypeAlloc,
         offset: usize,
@@ -1238,7 +1286,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn stream_new(
+    fn stream_new(
         &mut self,
         ty: u32,
         types: &mut TypeAlloc,
@@ -1262,7 +1310,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn stream_read(
+    fn stream_read(
         &mut self,
         ty: u32,
         options: &[CanonicalOption],
@@ -1294,7 +1342,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn stream_write(
+    fn stream_write(
         &mut self,
         ty: u32,
         options: &[CanonicalOption],
@@ -1324,7 +1372,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn stream_cancel_read(
+    fn stream_cancel_read(
         &mut self,
         ty: u32,
         _async_: bool,
@@ -1349,7 +1397,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn stream_cancel_write(
+    fn stream_cancel_write(
         &mut self,
         ty: u32,
         _async_: bool,
@@ -1374,7 +1422,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn stream_close_readable(
+    fn stream_close_readable(
         &mut self,
         ty: u32,
         types: &mut TypeAlloc,
@@ -1398,7 +1446,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn stream_close_writable(
+    fn stream_close_writable(
         &mut self,
         ty: u32,
         types: &mut TypeAlloc,
@@ -1422,7 +1470,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn future_new(
+    fn future_new(
         &mut self,
         ty: u32,
         types: &mut TypeAlloc,
@@ -1446,7 +1494,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn future_read(
+    fn future_read(
         &mut self,
         ty: u32,
         options: &[CanonicalOption],
@@ -1478,7 +1526,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn future_write(
+    fn future_write(
         &mut self,
         ty: u32,
         options: Vec<CanonicalOption>,
@@ -1508,7 +1556,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn future_cancel_read(
+    fn future_cancel_read(
         &mut self,
         ty: u32,
         _async_: bool,
@@ -1533,7 +1581,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn future_cancel_write(
+    fn future_cancel_write(
         &mut self,
         ty: u32,
         _async_: bool,
@@ -1558,7 +1606,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn future_close_readable(
+    fn future_close_readable(
         &mut self,
         ty: u32,
         types: &mut TypeAlloc,
@@ -1582,7 +1630,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn future_close_writable(
+    fn future_close_writable(
         &mut self,
         ty: u32,
         types: &mut TypeAlloc,
@@ -1606,7 +1654,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn error_context_new(
+    fn error_context_new(
         &mut self,
         options: Vec<CanonicalOption>,
         types: &mut TypeAlloc,
@@ -1630,7 +1678,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn error_context_debug_message(
+    fn error_context_debug_message(
         &mut self,
         options: Vec<CanonicalOption>,
         types: &mut TypeAlloc,
@@ -1654,7 +1702,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn error_context_drop(
+    fn error_context_drop(
         &mut self,
         types: &mut TypeAlloc,
         offset: usize,
@@ -1672,6 +1720,104 @@ impl ComponentState {
         Ok(())
     }
 
+    fn waitable_set_new(
+        &mut self,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        if !features.component_model_async() {
+            bail!(
+                offset,
+                "`waitable-set.new` requires the component model async feature"
+            )
+        }
+
+        self.core_funcs
+            .push(types.intern_func_type(FuncType::new([], [ValType::I32]), offset));
+        Ok(())
+    }
+
+    fn waitable_set_wait(
+        &mut self,
+        _async_: bool,
+        memory: u32,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        if !features.component_model_async() {
+            bail!(
+                offset,
+                "`waitable-set.wait` requires the component model async feature"
+            )
+        }
+
+        self.memory_at(memory, offset)?;
+
+        self.core_funcs
+            .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
+        Ok(())
+    }
+
+    fn waitable_set_poll(
+        &mut self,
+        _async_: bool,
+        memory: u32,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        if !features.component_model_async() {
+            bail!(
+                offset,
+                "`waitable-set.poll` requires the component model async feature"
+            )
+        }
+
+        self.memory_at(memory, offset)?;
+
+        self.core_funcs
+            .push(types.intern_func_type(FuncType::new([ValType::I32; 2], [ValType::I32]), offset));
+        Ok(())
+    }
+
+    fn waitable_set_drop(
+        &mut self,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        if !features.component_model_async() {
+            bail!(
+                offset,
+                "`waitable-set.drop` requires the component model async feature"
+            )
+        }
+
+        self.core_funcs
+            .push(types.intern_func_type(FuncType::new([ValType::I32], []), offset));
+        Ok(())
+    }
+
+    fn waitable_join(
+        &mut self,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        if !features.component_model_async() {
+            bail!(
+                offset,
+                "`waitable.join` requires the component model async feature"
+            )
+        }
+
+        self.core_funcs
+            .push(types.intern_func_type(FuncType::new([ValType::I32; 2], []), offset));
+        Ok(())
+    }
+
     fn check_local_resource(&self, idx: u32, types: &TypeList, offset: usize) -> Result<ValType> {
         let resource = self.resource_at(idx, types, offset)?;
         match self
@@ -1696,7 +1842,7 @@ impl ComponentState {
         bail!(offset, "type index {} is not a resource type", idx)
     }
 
-    pub fn thread_spawn(
+    fn thread_spawn(
         &mut self,
         func_ty_index: u32,
         types: &mut TypeAlloc,
@@ -1747,7 +1893,7 @@ impl ComponentState {
         Ok(())
     }
 
-    pub fn thread_available_parallelism(
+    fn thread_available_parallelism(
         &mut self,
         types: &mut TypeAlloc,
         offset: usize,
diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs
index 19861fa2..5913dc0f 100644
--- a/crates/wasmprinter/src/component.rs
+++ b/crates/wasmprinter/src/component.rs
@@ -928,26 +928,6 @@ impl Printer<'_, '_> {
                         Ok(())
                     })?;
                 }
-                CanonicalFunction::TaskWait { async_, memory } => {
-                    self.print_intrinsic(state, "canon task.wait ", &|me, state| {
-                        if async_ {
-                            me.result.write_str("async ")?;
-                        }
-                        me.start_group("memory ")?;
-                        me.print_idx(&state.core.memory_names, memory)?;
-                        me.end_group()
-                    })?;
-                }
-                CanonicalFunction::TaskPoll { async_, memory } => {
-                    self.print_intrinsic(state, "canon task.poll ", &|me, state| {
-                        if async_ {
-                            me.result.write_str("async ")?;
-                        }
-                        me.start_group("memory ")?;
-                        me.print_idx(&state.core.memory_names, memory)?;
-                        me.end_group()
-                    })?;
-                }
                 CanonicalFunction::TaskYield { async_ } => {
                     self.print_intrinsic(state, "canon task.yield", &|me, _| {
                         if async_ {
@@ -1064,6 +1044,35 @@ impl Printer<'_, '_> {
                 CanonicalFunction::ErrorContextDrop => {
                     self.print_intrinsic(state, "canon error-context.drop", &|_, _| Ok(()))?;
                 }
+                CanonicalFunction::WaitableSetNew => {
+                    self.print_intrinsic(state, "canon waitable-set.new", &|_, _| Ok(()))?;
+                }
+                CanonicalFunction::WaitableSetWait { async_, memory } => {
+                    self.print_intrinsic(state, "canon waitable-set.wait ", &|me, state| {
+                        if async_ {
+                            me.result.write_str("async ")?;
+                        }
+                        me.start_group("memory ")?;
+                        me.print_idx(&state.core.memory_names, memory)?;
+                        me.end_group()
+                    })?;
+                }
+                CanonicalFunction::WaitableSetPoll { async_, memory } => {
+                    self.print_intrinsic(state, "canon waitable-set.poll ", &|me, state| {
+                        if async_ {
+                            me.result.write_str("async ")?;
+                        }
+                        me.start_group("memory ")?;
+                        me.print_idx(&state.core.memory_names, memory)?;
+                        me.end_group()
+                    })?;
+                }
+                CanonicalFunction::WaitableSetDrop => {
+                    self.print_intrinsic(state, "canon waitable-set.drop", &|_, _| Ok(()))?;
+                }
+                CanonicalFunction::WaitableJoin => {
+                    self.print_intrinsic(state, "canon waitable.join", &|_, _| Ok(()))?;
+                }
             }
         }
 
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index 741d7735..423644db 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -370,14 +370,6 @@ impl<'a> Encoder<'a> {
                     info.opts.iter().map(Into::into),
                 );
             }
-            CanonicalFuncKind::TaskWait(info) => {
-                self.core_func_names.push(name);
-                self.funcs.task_wait(info.async_, info.memory.idx.into());
-            }
-            CanonicalFuncKind::TaskPoll(info) => {
-                self.core_func_names.push(name);
-                self.funcs.task_poll(info.async_, info.memory.idx.into());
-            }
             CanonicalFuncKind::TaskYield(info) => {
                 self.core_func_names.push(name);
                 self.funcs.task_yield(info.async_);
@@ -460,6 +452,28 @@ impl<'a> Encoder<'a> {
                 self.core_func_names.push(name);
                 self.funcs.error_context_drop();
             }
+            CanonicalFuncKind::WaitableSetNew => {
+                self.core_func_names.push(name);
+                self.funcs.waitable_set_new();
+            }
+            CanonicalFuncKind::WaitableSetWait(info) => {
+                self.core_func_names.push(name);
+                self.funcs
+                    .waitable_set_wait(info.async_, info.memory.idx.into());
+            }
+            CanonicalFuncKind::WaitableSetPoll(info) => {
+                self.core_func_names.push(name);
+                self.funcs
+                    .waitable_set_poll(info.async_, info.memory.idx.into());
+            }
+            CanonicalFuncKind::WaitableSetDrop => {
+                self.core_func_names.push(name);
+                self.funcs.waitable_set_drop();
+            }
+            CanonicalFuncKind::WaitableJoin => {
+                self.core_func_names.push(name);
+                self.funcs.waitable_join();
+            }
         }
 
         self.flush(Some(self.funcs.id()));
diff --git a/crates/wast/src/component/expand.rs b/crates/wast/src/component/expand.rs
index 72db0ec2..611dfbc8 100644
--- a/crates/wast/src/component/expand.rs
+++ b/crates/wast/src/component/expand.rs
@@ -264,35 +264,7 @@ impl<'a> Expander<'a> {
             CanonicalFuncKind::Lift { ty, .. } => {
                 self.expand_component_type_use(ty);
             }
-            CanonicalFuncKind::Lower(_)
-            | CanonicalFuncKind::ResourceNew(_)
-            | CanonicalFuncKind::ResourceRep(_)
-            | CanonicalFuncKind::ResourceDrop(_)
-            | CanonicalFuncKind::ThreadSpawn(_)
-            | CanonicalFuncKind::ThreadAvailableParallelism(_)
-            | CanonicalFuncKind::BackpressureSet
-            | CanonicalFuncKind::TaskReturn(_)
-            | CanonicalFuncKind::TaskWait(_)
-            | CanonicalFuncKind::TaskPoll(_)
-            | CanonicalFuncKind::TaskYield(_)
-            | CanonicalFuncKind::SubtaskDrop
-            | CanonicalFuncKind::StreamNew(_)
-            | CanonicalFuncKind::StreamRead(_)
-            | CanonicalFuncKind::StreamWrite(_)
-            | CanonicalFuncKind::StreamCancelRead(_)
-            | CanonicalFuncKind::StreamCancelWrite(_)
-            | CanonicalFuncKind::StreamCloseReadable(_)
-            | CanonicalFuncKind::StreamCloseWritable(_)
-            | CanonicalFuncKind::FutureNew(_)
-            | CanonicalFuncKind::FutureRead(_)
-            | CanonicalFuncKind::FutureWrite(_)
-            | CanonicalFuncKind::FutureCancelRead(_)
-            | CanonicalFuncKind::FutureCancelWrite(_)
-            | CanonicalFuncKind::FutureCloseReadable(_)
-            | CanonicalFuncKind::FutureCloseWritable(_)
-            | CanonicalFuncKind::ErrorContextNew(_)
-            | CanonicalFuncKind::ErrorContextDebugMessage(_)
-            | CanonicalFuncKind::ErrorContextDrop => {}
+            _ => {}
         }
     }
 
@@ -358,18 +330,6 @@ impl<'a> Expander<'a> {
                 name: func.name,
                 kind: CanonicalFuncKind::TaskReturn(info),
             }),
-            CoreFuncKind::TaskWait(info) => ComponentField::CanonicalFunc(CanonicalFunc {
-                span: func.span,
-                id: func.id,
-                name: func.name,
-                kind: CanonicalFuncKind::TaskWait(info),
-            }),
-            CoreFuncKind::TaskPoll(info) => ComponentField::CanonicalFunc(CanonicalFunc {
-                span: func.span,
-                id: func.id,
-                name: func.name,
-                kind: CanonicalFuncKind::TaskPoll(info),
-            }),
             CoreFuncKind::TaskYield(info) => ComponentField::CanonicalFunc(CanonicalFunc {
                 span: func.span,
                 id: func.id,
@@ -494,6 +454,36 @@ impl<'a> Expander<'a> {
                 name: func.name,
                 kind: CanonicalFuncKind::ErrorContextDrop,
             }),
+            CoreFuncKind::WaitableSetNew => ComponentField::CanonicalFunc(CanonicalFunc {
+                span: func.span,
+                id: func.id,
+                name: func.name,
+                kind: CanonicalFuncKind::WaitableSetNew,
+            }),
+            CoreFuncKind::WaitableSetWait(info) => ComponentField::CanonicalFunc(CanonicalFunc {
+                span: func.span,
+                id: func.id,
+                name: func.name,
+                kind: CanonicalFuncKind::WaitableSetWait(info),
+            }),
+            CoreFuncKind::WaitableSetPoll(info) => ComponentField::CanonicalFunc(CanonicalFunc {
+                span: func.span,
+                id: func.id,
+                name: func.name,
+                kind: CanonicalFuncKind::WaitableSetPoll(info),
+            }),
+            CoreFuncKind::WaitableSetDrop => ComponentField::CanonicalFunc(CanonicalFunc {
+                span: func.span,
+                id: func.id,
+                name: func.name,
+                kind: CanonicalFuncKind::WaitableSetDrop,
+            }),
+            CoreFuncKind::WaitableJoin => ComponentField::CanonicalFunc(CanonicalFunc {
+                span: func.span,
+                id: func.id,
+                name: func.name,
+                kind: CanonicalFuncKind::WaitableJoin,
+            }),
         }
     }
 
diff --git a/crates/wast/src/component/func.rs b/crates/wast/src/component/func.rs
index be025d0a..5bd45222 100644
--- a/crates/wast/src/component/func.rs
+++ b/crates/wast/src/component/func.rs
@@ -55,8 +55,6 @@ pub enum CoreFuncKind<'a> {
     ThreadAvailableParallelism(CanonThreadAvailableParallelism),
     BackpressureSet,
     TaskReturn(CanonTaskReturn<'a>),
-    TaskWait(CanonTaskWait<'a>),
-    TaskPoll(CanonTaskPoll<'a>),
     TaskYield(CanonTaskYield),
     SubtaskDrop,
     StreamNew(CanonStreamNew<'a>),
@@ -76,6 +74,11 @@ pub enum CoreFuncKind<'a> {
     ErrorContextNew(CanonErrorContextNew<'a>),
     ErrorContextDebugMessage(CanonErrorContextDebugMessage<'a>),
     ErrorContextDrop,
+    WaitableSetNew,
+    WaitableSetWait(CanonWaitableSetWait<'a>),
+    WaitableSetPoll(CanonWaitableSetPoll<'a>),
+    WaitableSetDrop,
+    WaitableJoin,
 }
 
 impl<'a> Parse<'a> for CoreFuncKind<'a> {
@@ -107,10 +110,6 @@ impl<'a> Parse<'a> for CoreFuncKind<'a> {
                 Ok(CoreFuncKind::BackpressureSet)
             } else if l.peek::<kw::task_return>()? {
                 Ok(CoreFuncKind::TaskReturn(parser.parse()?))
-            } else if l.peek::<kw::task_wait>()? {
-                Ok(CoreFuncKind::TaskWait(parser.parse()?))
-            } else if l.peek::<kw::task_poll>()? {
-                Ok(CoreFuncKind::TaskPoll(parser.parse()?))
             } else if l.peek::<kw::task_yield>()? {
                 Ok(CoreFuncKind::TaskYield(parser.parse()?))
             } else if l.peek::<kw::subtask_drop>()? {
@@ -151,6 +150,19 @@ impl<'a> Parse<'a> for CoreFuncKind<'a> {
             } else if l.peek::<kw::error_context_drop>()? {
                 parser.parse::<kw::error_context_drop>()?;
                 Ok(CoreFuncKind::ErrorContextDrop)
+            } else if l.peek::<kw::waitable_set_new>()? {
+                parser.parse::<kw::waitable_set_new>()?;
+                Ok(CoreFuncKind::WaitableSetNew)
+            } else if l.peek::<kw::waitable_set_wait>()? {
+                Ok(CoreFuncKind::WaitableSetWait(parser.parse()?))
+            } else if l.peek::<kw::waitable_set_poll>()? {
+                Ok(CoreFuncKind::WaitableSetPoll(parser.parse()?))
+            } else if l.peek::<kw::waitable_set_drop>()? {
+                parser.parse::<kw::waitable_set_drop>()?;
+                Ok(CoreFuncKind::WaitableSetDrop)
+            } else if l.peek::<kw::waitable_join>()? {
+                parser.parse::<kw::waitable_join>()?;
+                Ok(CoreFuncKind::WaitableJoin)
             } else {
                 Err(l.error())
             }
@@ -345,8 +357,6 @@ pub enum CanonicalFuncKind<'a> {
 
     BackpressureSet,
     TaskReturn(CanonTaskReturn<'a>),
-    TaskWait(CanonTaskWait<'a>),
-    TaskPoll(CanonTaskPoll<'a>),
     TaskYield(CanonTaskYield),
     SubtaskDrop,
     StreamNew(CanonStreamNew<'a>),
@@ -366,6 +376,11 @@ pub enum CanonicalFuncKind<'a> {
     ErrorContextNew(CanonErrorContextNew<'a>),
     ErrorContextDebugMessage(CanonErrorContextDebugMessage<'a>),
     ErrorContextDrop,
+    WaitableSetNew,
+    WaitableSetWait(CanonWaitableSetWait<'a>),
+    WaitableSetPoll(CanonWaitableSetPoll<'a>),
+    WaitableSetDrop,
+    WaitableJoin,
 }
 
 /// Information relating to lifting a core function.
@@ -548,9 +563,9 @@ impl<'a> Parse<'a> for CanonTaskReturn<'a> {
     }
 }
 
-/// Information relating to the `task.wait` intrinsic.
+/// Information relating to the `waitable-set.wait` intrinsic.
 #[derive(Debug)]
-pub struct CanonTaskWait<'a> {
+pub struct CanonWaitableSetWait<'a> {
     /// If true, the component instance may be reentered during a call to this
     /// intrinsic.
     pub async_: bool,
@@ -558,9 +573,9 @@ pub struct CanonTaskWait<'a> {
     pub memory: CoreItemRef<'a, kw::memory>,
 }
 
-impl<'a> Parse<'a> for CanonTaskWait<'a> {
+impl<'a> Parse<'a> for CanonWaitableSetWait<'a> {
     fn parse(parser: Parser<'a>) -> Result<Self> {
-        parser.parse::<kw::task_wait>()?;
+        parser.parse::<kw::waitable_set_wait>()?;
         let async_ = parser.parse::<Option<kw::r#async>>()?.is_some();
         let memory = parser.parens(|parser| {
             let span = parser.parse::<kw::memory>()?.0;
@@ -571,9 +586,9 @@ impl<'a> Parse<'a> for CanonTaskWait<'a> {
     }
 }
 
-/// Information relating to the `task.poll` intrinsic.
+/// Information relating to the `waitable-set.poll` intrinsic.
 #[derive(Debug)]
-pub struct CanonTaskPoll<'a> {
+pub struct CanonWaitableSetPoll<'a> {
     /// If true, the component instance may be reentered during a call to this
     /// intrinsic.
     pub async_: bool,
@@ -581,9 +596,9 @@ pub struct CanonTaskPoll<'a> {
     pub memory: CoreItemRef<'a, kw::memory>,
 }
 
-impl<'a> Parse<'a> for CanonTaskPoll<'a> {
+impl<'a> Parse<'a> for CanonWaitableSetPoll<'a> {
     fn parse(parser: Parser<'a>) -> Result<Self> {
-        parser.parse::<kw::task_poll>()?;
+        parser.parse::<kw::waitable_set_poll>()?;
         let async_ = parser.parse::<Option<kw::r#async>>()?.is_some();
         let memory = parser.parens(|parser| {
             let span = parser.parse::<kw::memory>()?.0;
diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs
index 8ca18cdc..f3ff0a1d 100644
--- a/crates/wast/src/component/resolve.rs
+++ b/crates/wast/src/component/resolve.rs
@@ -397,12 +397,6 @@ impl<'a> Resolver<'a> {
                 }
                 self.canon_opts(&mut info.opts)?;
             }
-            CanonicalFuncKind::TaskWait(info) => {
-                self.core_item_ref(&mut info.memory)?;
-            }
-            CanonicalFuncKind::TaskPoll(info) => {
-                self.core_item_ref(&mut info.memory)?;
-            }
             CanonicalFuncKind::StreamNew(info) => {
                 self.resolve_ns(&mut info.ty, Ns::Type)?;
             }
@@ -455,6 +449,15 @@ impl<'a> Resolver<'a> {
             CanonicalFuncKind::ErrorContextDebugMessage(info) => {
                 self.canon_opts(&mut info.opts)?;
             }
+            CanonicalFuncKind::WaitableSetNew => {}
+            CanonicalFuncKind::WaitableSetWait(info) => {
+                self.core_item_ref(&mut info.memory)?;
+            }
+            CanonicalFuncKind::WaitableSetPoll(info) => {
+                self.core_item_ref(&mut info.memory)?;
+            }
+            CanonicalFuncKind::WaitableSetDrop => {}
+            CanonicalFuncKind::WaitableJoin => {}
         }
 
         Ok(())
@@ -968,8 +971,6 @@ impl<'a> ComponentState<'a> {
                 | CanonicalFuncKind::ThreadAvailableParallelism(_)
                 | CanonicalFuncKind::BackpressureSet
                 | CanonicalFuncKind::TaskReturn(_)
-                | CanonicalFuncKind::TaskWait(_)
-                | CanonicalFuncKind::TaskPoll(_)
                 | CanonicalFuncKind::TaskYield(_)
                 | CanonicalFuncKind::SubtaskDrop
                 | CanonicalFuncKind::StreamNew(_)
@@ -988,9 +989,12 @@ impl<'a> ComponentState<'a> {
                 | CanonicalFuncKind::FutureCloseWritable(_)
                 | CanonicalFuncKind::ErrorContextNew(_)
                 | CanonicalFuncKind::ErrorContextDebugMessage(_)
-                | CanonicalFuncKind::ErrorContextDrop => {
-                    self.core_funcs.register(f.id, "core func")?
-                }
+                | CanonicalFuncKind::ErrorContextDrop
+                | CanonicalFuncKind::WaitableSetNew
+                | CanonicalFuncKind::WaitableSetWait(_)
+                | CanonicalFuncKind::WaitableSetPoll(_)
+                | CanonicalFuncKind::WaitableSetDrop
+                | CanonicalFuncKind::WaitableJoin => self.core_funcs.register(f.id, "core func")?,
             },
             ComponentField::CoreFunc(_) | ComponentField::Func(_) => {
                 unreachable!("should be expanded already")
diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs
index e8bb5723..9f294891 100644
--- a/crates/wast/src/lib.rs
+++ b/crates/wast/src/lib.rs
@@ -558,8 +558,6 @@ pub mod kw {
     custom_keyword!(thread_available_parallelism = "thread.available_parallelism");
     custom_keyword!(backpressure_set = "backpressure.set");
     custom_keyword!(task_return = "task.return");
-    custom_keyword!(task_wait = "task.wait");
-    custom_keyword!(task_poll = "task.poll");
     custom_keyword!(task_yield = "task.yield");
     custom_keyword!(subtask_drop = "subtask.drop");
     custom_keyword!(stream_new = "stream.new");
@@ -586,6 +584,11 @@ pub mod kw {
     custom_keyword!(stream);
     custom_keyword!(future);
     custom_keyword!(error_context = "error-context");
+    custom_keyword!(waitable_set_new = "waitable-set.new");
+    custom_keyword!(waitable_set_wait = "waitable-set.wait");
+    custom_keyword!(waitable_set_poll = "waitable-set.poll");
+    custom_keyword!(waitable_set_drop = "waitable-set.drop");
+    custom_keyword!(waitable_join = "waitable.join");
 }
 
 /// Common annotations used to parse WebAssembly text files.
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index 82a9ad15..b504b458 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -1314,12 +1314,12 @@ impl<'a> EncodingState<'a> {
                     }
                 }
 
-                ShimKind::TaskWait { async_ } => self
+                ShimKind::WaitableSetWait { async_ } => self
                     .component
-                    .task_wait(*async_, self.memory_index.unwrap()),
-                ShimKind::TaskPoll { async_ } => self
+                    .waitable_set_wait(*async_, self.memory_index.unwrap()),
+                ShimKind::WaitableSetPoll { async_ } => self
                     .component
-                    .task_poll(*async_, self.memory_index.unwrap()),
+                    .waitable_set_poll(*async_, self.memory_index.unwrap()),
                 ShimKind::ErrorContextNew { encoding } => self.component.error_context_new(
                     shim.options.into_iter(*encoding, self.memory_index, None)?,
                 ),
@@ -1644,11 +1644,13 @@ impl<'a> EncodingState<'a> {
                 let index = self.component.backpressure_set();
                 Ok((ExportKind::Func, index))
             }
-            Import::TaskWait { async_ } => {
-                Ok(self.materialize_shim_import(shims, &ShimKind::TaskWait { async_: *async_ }))
+            Import::WaitableSetWait { async_ } => {
+                Ok(self
+                    .materialize_shim_import(shims, &ShimKind::WaitableSetWait { async_: *async_ }))
             }
-            Import::TaskPoll { async_ } => {
-                Ok(self.materialize_shim_import(shims, &ShimKind::TaskPoll { async_: *async_ }))
+            Import::WaitableSetPoll { async_ } => {
+                Ok(self
+                    .materialize_shim_import(shims, &ShimKind::WaitableSetPoll { async_: *async_ }))
             }
             Import::TaskYield { async_ } => {
                 let index = self.component.task_yield(*async_);
@@ -1762,6 +1764,19 @@ impl<'a> EncodingState<'a> {
                 key,
                 *abi,
             ),
+
+            Import::WaitableSetNew => {
+                let index = self.component.waitable_set_new();
+                Ok((ExportKind::Func, index))
+            }
+            Import::WaitableSetDrop => {
+                let index = self.component.waitable_set_drop();
+                Ok((ExportKind::Func, index))
+            }
+            Import::WaitableJoin => {
+                let index = self.component.waitable_join();
+                Ok((ExportKind::Func, index))
+            }
         }
     }
 
@@ -2039,12 +2054,14 @@ enum ShimKind<'a> {
         /// Which variation of `{stream|future}.{read|write}` we're emitting.
         kind: PayloadFuncKind,
     },
-    /// A shim used for the `task.wait` built-in function, which must refer to
-    /// the core module instance's memory to which results will be written.
-    TaskWait { async_: bool },
-    /// A shim used for the `task.poll` built-in function, which must refer to
-    /// the core module instance's memory to which results will be written.
-    TaskPoll { async_: bool },
+    /// A shim used for the `waitable-set.wait` built-in function, which must
+    /// refer to the core module instance's memory to which results will be
+    /// written.
+    WaitableSetWait { async_: bool },
+    /// A shim used for the `waitable-set.poll` built-in function, which must
+    /// refer to the core module instance's memory to which results will be
+    /// written.
+    WaitableSetPoll { async_: bool },
     /// Shim for `task.return` to handle a reference to a `memory` which may
     TaskReturn {
         /// The interface (optional) that owns `func` below. If `None` then it's
@@ -2138,7 +2155,10 @@ impl<'a> Shims<'a> {
                 | Import::StreamCancelRead { .. }
                 | Import::StreamCancelWrite { .. }
                 | Import::StreamCloseWritable { .. }
-                | Import::StreamCloseReadable { .. } => {}
+                | Import::StreamCloseReadable { .. }
+                | Import::WaitableSetNew
+                | Import::WaitableSetDrop
+                | Import::WaitableJoin => {}
 
                 // If `task.return` needs to be indirect then generate a shim
                 // for it, otherwise skip the shim and let it get materialized
@@ -2223,15 +2243,15 @@ impl<'a> Shims<'a> {
                     );
                 }
 
-                Import::TaskWait { async_ } => {
+                Import::WaitableSetWait { async_ } => {
                     let name = self.shims.len().to_string();
                     self.push(Shim {
                         name,
-                        debug_name: "task-wait".to_string(),
+                        debug_name: "waitable-set.wait".to_string(),
                         options: RequiredOptions::empty(),
-                        kind: ShimKind::TaskWait { async_: *async_ },
+                        kind: ShimKind::WaitableSetWait { async_: *async_ },
                         sig: WasmSignature {
-                            params: vec![WasmType::I32],
+                            params: vec![WasmType::I32; 2],
                             results: vec![WasmType::I32],
                             indirect_params: false,
                             retptr: false,
@@ -2239,15 +2259,15 @@ impl<'a> Shims<'a> {
                     });
                 }
 
-                Import::TaskPoll { async_ } => {
+                Import::WaitableSetPoll { async_ } => {
                     let name = self.shims.len().to_string();
                     self.push(Shim {
                         name,
-                        debug_name: "task-poll".to_string(),
+                        debug_name: "waitable-set.poll".to_string(),
                         options: RequiredOptions::empty(),
-                        kind: ShimKind::TaskPoll { async_: *async_ },
+                        kind: ShimKind::WaitableSetPoll { async_: *async_ },
                         sig: WasmSignature {
-                            params: vec![WasmType::I32],
+                            params: vec![WasmType::I32; 2],
                             results: vec![WasmType::I32],
                             indirect_params: false,
                             retptr: false,
diff --git a/crates/wit-component/src/validation.rs b/crates/wit-component/src/validation.rs
index d436ac83..3dc2ad76 100644
--- a/crates/wit-component/src/validation.rs
+++ b/crates/wit-component/src/validation.rs
@@ -261,19 +261,28 @@ pub enum Import {
     /// additional concurrent calls.
     BackpressureSet,
 
-    /// A `canon task.wait` intrinsic.
+    /// A `waitable-set.new` intrinsic.
+    WaitableSetNew,
+
+    /// A `canon waitable-set.wait` intrinsic.
     ///
     /// This allows the guest to wait for any pending calls to async-lowered
     /// imports and/or `stream` and `future` operations to complete without
     /// unwinding the current Wasm stack.
-    TaskWait { async_: bool },
+    WaitableSetWait { async_: bool },
 
-    /// A `canon task.poll` intrinsic.
+    /// A `canon waitable.poll` intrinsic.
     ///
     /// This allows the guest to check whether any pending calls to
     /// async-lowered imports and/or `stream` and `future` operations have
     /// completed without unwinding the current Wasm stack and without blocking.
-    TaskPoll { async_: bool },
+    WaitableSetPoll { async_: bool },
+
+    /// A `waitable-set.drop` intrinsic.
+    WaitableSetDrop,
+
+    /// A `waitable.join` intrinsic.
+    WaitableJoin,
 
     /// A `canon task.wait` intrinsic.
     ///
@@ -542,36 +551,65 @@ impl ImportMap {
         } else {
             AbiVariant::GuestImport
         };
+        let validate_not_async = || {
+            if async_ {
+                bail!("`{name}` cannot be marked `async`")
+            }
+            Ok(())
+        };
 
         if module == names.import_root() {
             if Some(name) == names.error_context_drop() {
+                validate_not_async()?;
                 let expected = FuncType::new([ValType::I32], []);
                 validate_func_sig(name, &expected, ty)?;
                 return Ok(Import::ErrorContextDrop);
             }
 
             if Some(name) == names.backpressure_set() {
+                validate_not_async()?;
                 let expected = FuncType::new([ValType::I32], []);
                 validate_func_sig(name, &expected, ty)?;
                 return Ok(Import::BackpressureSet);
             }
 
-            if Some(name) == names.task_wait() {
-                let expected = FuncType::new([ValType::I32], [ValType::I32]);
+            if Some(name) == names.waitable_set_new() {
+                validate_not_async()?;
+                let expected = FuncType::new([], [ValType::I32]);
                 validate_func_sig(name, &expected, ty)?;
-                return Ok(Import::TaskWait {
+                return Ok(Import::WaitableSetNew);
+            }
+
+            if Some(name) == names.waitable_set_wait() {
+                let expected = FuncType::new([ValType::I32; 2], [ValType::I32]);
+                validate_func_sig(name, &expected, ty)?;
+                return Ok(Import::WaitableSetWait {
                     async_: abi == AbiVariant::GuestImportAsync,
                 });
             }
 
-            if Some(name) == names.task_poll() {
-                let expected = FuncType::new([ValType::I32], [ValType::I32]);
+            if Some(name) == names.waitable_set_poll() {
+                let expected = FuncType::new([ValType::I32; 2], [ValType::I32]);
                 validate_func_sig(name, &expected, ty)?;
-                return Ok(Import::TaskPoll {
+                return Ok(Import::WaitableSetPoll {
                     async_: abi == AbiVariant::GuestImportAsync,
                 });
             }
 
+            if Some(name) == names.waitable_set_drop() {
+                validate_not_async()?;
+                let expected = FuncType::new([ValType::I32], []);
+                validate_func_sig(name, &expected, ty)?;
+                return Ok(Import::WaitableSetDrop);
+            }
+
+            if Some(name) == names.waitable_join() {
+                validate_not_async()?;
+                let expected = FuncType::new([ValType::I32; 2], []);
+                validate_func_sig(name, &expected, ty)?;
+                return Ok(Import::WaitableJoin);
+            }
+
             if Some(name) == names.task_yield() {
                 let expected = FuncType::new([], []);
                 validate_func_sig(name, &expected, ty)?;
@@ -581,18 +619,21 @@ impl ImportMap {
             }
 
             if Some(name) == names.subtask_drop() {
+                validate_not_async()?;
                 let expected = FuncType::new([ValType::I32], []);
                 validate_func_sig(name, &expected, ty)?;
                 return Ok(Import::SubtaskDrop);
             }
 
             if let Some(encoding) = names.error_context_new(name) {
+                validate_not_async()?;
                 let expected = FuncType::new([ValType::I32; 2], [ValType::I32]);
                 validate_func_sig(name, &expected, ty)?;
                 return Ok(Import::ErrorContextNew { encoding });
             }
 
             if let Some((encoding, realloc)) = names.error_context_debug_message(name) {
+                validate_not_async()?;
                 let expected = FuncType::new([ValType::I32; 2], []);
                 validate_func_sig(name, &expected, ty)?;
                 return Ok(Import::ErrorContextDebugMessage {
@@ -1367,8 +1408,11 @@ trait NameMangling {
     fn resource_rep_name<'a>(&self, s: &'a str) -> Option<&'a str>;
     fn task_return_name<'a>(&self, s: &'a str) -> Option<&'a str>;
     fn backpressure_set(&self) -> Option<&str>;
-    fn task_wait(&self) -> Option<&str>;
-    fn task_poll(&self) -> Option<&str>;
+    fn waitable_set_new(&self) -> Option<&str>;
+    fn waitable_set_wait(&self) -> Option<&str>;
+    fn waitable_set_poll(&self) -> Option<&str>;
+    fn waitable_set_drop(&self) -> Option<&str>;
+    fn waitable_join(&self) -> Option<&str>;
     fn task_yield(&self) -> Option<&str>;
     fn subtask_drop(&self) -> Option<&str>;
     fn callback_name<'a>(&self, s: &'a str) -> Option<&'a str>;
@@ -1441,10 +1485,19 @@ impl NameMangling for Standard {
     fn backpressure_set(&self) -> Option<&str> {
         None
     }
-    fn task_wait(&self) -> Option<&str> {
+    fn waitable_set_new(&self) -> Option<&str> {
+        None
+    }
+    fn waitable_set_wait(&self) -> Option<&str> {
         None
     }
-    fn task_poll(&self) -> Option<&str> {
+    fn waitable_set_poll(&self) -> Option<&str> {
+        None
+    }
+    fn waitable_set_drop(&self) -> Option<&str> {
+        None
+    }
+    fn waitable_join(&self) -> Option<&str> {
         None
     }
     fn task_yield(&self) -> Option<&str> {
@@ -1617,11 +1670,20 @@ impl NameMangling for Legacy {
     fn backpressure_set(&self) -> Option<&str> {
         Some("[backpressure-set]")
     }
-    fn task_wait(&self) -> Option<&str> {
-        Some("[task-wait]")
+    fn waitable_set_new(&self) -> Option<&str> {
+        Some("[waitable-set-new]")
+    }
+    fn waitable_set_wait(&self) -> Option<&str> {
+        Some("[waitable-set-wait]")
+    }
+    fn waitable_set_poll(&self) -> Option<&str> {
+        Some("[waitable-set-poll]")
+    }
+    fn waitable_set_drop(&self) -> Option<&str> {
+        Some("[waitable-set-drop]")
     }
-    fn task_poll(&self) -> Option<&str> {
-        Some("[task-poll]")
+    fn waitable_join(&self) -> Option<&str> {
+        Some("[waitable-join]")
     }
     fn task_yield(&self) -> Option<&str> {
         Some("[task-yield]")
diff --git a/src/bin/wasm-tools/dump.rs b/src/bin/wasm-tools/dump.rs
index e133a0fe..ad06ffe1 100644
--- a/src/bin/wasm-tools/dump.rs
+++ b/src/bin/wasm-tools/dump.rs
@@ -423,8 +423,11 @@ impl<'a> Dump<'a> {
                             | CanonicalFunction::ThreadAvailableParallelism
                             | CanonicalFunction::BackpressureSet
                             | CanonicalFunction::TaskReturn { .. }
-                            | CanonicalFunction::TaskWait { .. }
-                            | CanonicalFunction::TaskPoll { .. }
+                            | CanonicalFunction::WaitableSetNew
+                            | CanonicalFunction::WaitableSetWait { .. }
+                            | CanonicalFunction::WaitableSetPoll { .. }
+                            | CanonicalFunction::WaitableSetDrop
+                            | CanonicalFunction::WaitableJoin
                             | CanonicalFunction::TaskYield { .. }
                             | CanonicalFunction::SubtaskDrop
                             | CanonicalFunction::StreamNew { .. }


Test patch:
diff --git a/crates/wit-component/tests/components/async-builtins/component.wat b/crates/wit-component/tests/components/async-builtins/component.wat
index 88fd64fd..ae1c15d6 100644
--- a/crates/wit-component/tests/components/async-builtins/component.wat
+++ b/crates/wit-component/tests/components/async-builtins/component.wat
@@ -2,32 +2,35 @@
   (core module (;0;)
     (type (;0;) (func (param i32)))
     (type (;1;) (func (param i32 i32)))
-    (type (;2;) (func (param i32) (result i32)))
-    (type (;3;) (func))
-    (type (;4;) (func (param i32 i32) (result i32)))
+    (type (;2;) (func (result i32)))
+    (type (;3;) (func (param i32 i32) (result i32)))
+    (type (;4;) (func))
     (type (;5;) (func (param i32 i32 i32 i32) (result i32)))
     (import "$root" "[backpressure-set]" (func (;0;) (type 0)))
     (import "[export]$root" "[task-return]foo" (func (;1;) (type 1)))
     (import "[export]foo:foo/bar" "[task-return]foo" (func (;2;) (type 1)))
-    (import "$root" "[task-wait]" (func (;3;) (type 2)))
-    (import "$root" "[task-poll]" (func (;4;) (type 2)))
-    (import "$root" "[task-yield]" (func (;5;) (type 3)))
-    (import "$root" "[subtask-drop]" (func (;6;) (type 0)))
-    (import "$root" "[error-context-new;encoding=utf8]" (func (;7;) (type 4)))
-    (import "$root" "[error-context-debug-message;encoding=utf8;realloc=cabi_realloc]" (func (;8;) (type 1)))
-    (import "$root" "[error-context-drop]" (func (;9;) (type 0)))
+    (import "$root" "[waitable-set-new]" (func (;3;) (type 2)))
+    (import "$root" "[waitable-set-wait]" (func (;4;) (type 3)))
+    (import "$root" "[waitable-set-poll]" (func (;5;) (type 3)))
+    (import "$root" "[waitable-set-drop]" (func (;6;) (type 0)))
+    (import "$root" "[waitable-join]" (func (;7;) (type 1)))
+    (import "$root" "[task-yield]" (func (;8;) (type 4)))
+    (import "$root" "[subtask-drop]" (func (;9;) (type 0)))
+    (import "$root" "[error-context-new;encoding=utf8]" (func (;10;) (type 3)))
+    (import "$root" "[error-context-debug-message;encoding=utf8;realloc=cabi_realloc]" (func (;11;) (type 1)))
+    (import "$root" "[error-context-drop]" (func (;12;) (type 0)))
     (memory (;0;) 1)
-    (export "[async-stackful]foo" (func 10))
-    (export "[async-stackful]foo:foo/bar#foo" (func 11))
+    (export "[async-stackful]foo" (func 13))
+    (export "[async-stackful]foo:foo/bar#foo" (func 14))
     (export "memory" (memory 0))
-    (export "cabi_realloc" (func 12))
-    (func (;10;) (type 1) (param i32 i32)
+    (export "cabi_realloc" (func 15))
+    (func (;13;) (type 1) (param i32 i32)
       unreachable
     )
-    (func (;11;) (type 1) (param i32 i32)
+    (func (;14;) (type 1) (param i32 i32)
       unreachable
     )
-    (func (;12;) (type 5) (param i32 i32 i32 i32) (result i32)
+    (func (;15;) (type 5) (param i32 i32 i32 i32) (result i32)
       unreachable
     )
     (@producers
@@ -36,59 +39,59 @@
     )
   )
   (core module (;1;)
-    (type (;0;) (func (param i32) (result i32)))
-    (type (;1;) (func (param i32 i32) (result i32)))
+    (type (;0;) (func (param i32 i32) (result i32)))
+    (type (;1;) (func (param i32 i32)))
     (type (;2;) (func (param i32 i32)))
-    (type (;3;) (func (param i32 i32)))
     (table (;0;) 5 5 funcref)
-    (export "0" (func $task-wait))
-    (export "1" (func $task-poll))
+    (export "0" (func $waitable-set.wait))
+    (export "1" (func $waitable-set.poll))
     (export "2" (func $error-new))
     (export "3" (func $error-debug-message))
     (export "4" (func $task-return-foo))
     (export "$imports" (table 0))
-    (func $task-wait (;0;) (type 0) (param i32) (result i32)
+    (func $waitable-set.wait (;0;) (type 0) (param i32 i32) (result i32)
       local.get 0
+      local.get 1
       i32.const 0
       call_indirect (type 0)
     )
-    (func $task-poll (;1;) (type 0) (param i32) (result i32)
+    (func $waitable-set.poll (;1;) (type 0) (param i32 i32) (result i32)
       local.get 0
+      local.get 1
       i32.const 1
       call_indirect (type 0)
     )
-    (func $error-new (;2;) (type 1) (param i32 i32) (result i32)
+    (func $error-new (;2;) (type 0) (param i32 i32) (result i32)
       local.get 0
       local.get 1
       i32.const 2
-      call_indirect (type 1)
+      call_indirect (type 0)
     )
-    (func $error-debug-message (;3;) (type 2) (param i32 i32)
+    (func $error-debug-message (;3;) (type 1) (param i32 i32)
       local.get 0
       local.get 1
       i32.const 3
-      call_indirect (type 2)
+      call_indirect (type 1)
     )
-    (func $task-return-foo (;4;) (type 3) (param i32 i32)
+    (func $task-return-foo (;4;) (type 2) (param i32 i32)
       local.get 0
       local.get 1
       i32.const 4
-      call_indirect (type 3)
+      call_indirect (type 2)
     )
     (@producers
       (processed-by "wit-component" "$CARGO_PKG_VERSION")
     )
   )
   (core module (;2;)
-    (type (;0;) (func (param i32) (result i32)))
-    (type (;1;) (func (param i32 i32) (result i32)))
+    (type (;0;) (func (param i32 i32) (result i32)))
+    (type (;1;) (func (param i32 i32)))
     (type (;2;) (func (param i32 i32)))
-    (type (;3;) (func (param i32 i32)))
     (import "" "0" (func (;0;) (type 0)))
     (import "" "1" (func (;1;) (type 0)))
-    (import "" "2" (func (;2;) (type 1)))
-    (import "" "3" (func (;3;) (type 2)))
-    (import "" "4" (func (;4;) (type 3)))
+    (import "" "2" (func (;2;) (type 0)))
+    (import "" "3" (func (;3;) (type 1)))
+    (import "" "4" (func (;4;) (type 2)))
     (import "" "$imports" (table (;0;) 5 5 funcref))
     (elem (;0;) (i32.const 0) func 0 1 2 3 4)
     (@producers
@@ -97,29 +100,35 @@
   )
   (core instance (;0;) (instantiate 1))
   (core func (;0;) (canon backpressure.set))
-  (alias core export 0 "0" (core func (;1;)))
-  (alias core export 0 "1" (core func (;2;)))
-  (core func (;3;) (canon task.yield))
-  (core func (;4;) (canon subtask.drop))
-  (alias core export 0 "2" (core func (;5;)))
-  (alias core export 0 "3" (core func (;6;)))
-  (core func (;7;) (canon error-context.drop))
+  (core func (;1;) (canon waitable-set.new))
+  (alias core export 0 "0" (core func (;2;)))
+  (alias core export 0 "1" (core func (;3;)))
+  (core func (;4;) (canon waitable-set.drop))
+  (core func (;5;) (canon waitable.join))
+  (core func (;6;) (canon task.yield))
+  (core func (;7;) (canon subtask.drop))
+  (alias core export 0 "2" (core func (;8;)))
+  (alias core export 0 "3" (core func (;9;)))
+  (core func (;10;) (canon error-context.drop))
   (core instance (;1;)
     (export "[backpressure-set]" (func 0))
-    (export "[task-wait]" (func 1))
-    (export "[task-poll]" (func 2))
-    (export "[task-yield]" (func 3))
-    (export "[subtask-drop]" (func 4))
-    (export "[error-context-new;encoding=utf8]" (func 5))
-    (export "[error-context-debug-message;encoding=utf8;realloc=cabi_realloc]" (func 6))
-    (export "[error-context-drop]" (func 7))
+    (export "[waitable-set-new]" (func 1))
+    (export "[waitable-set-wait]" (func 2))
+    (export "[waitable-set-poll]" (func 3))
+    (export "[waitable-set-drop]" (func 4))
+    (export "[waitable-join]" (func 5))
+    (export "[task-yield]" (func 6))
+    (export "[subtask-drop]" (func 7))
+    (export "[error-context-new;encoding=utf8]" (func 8))
+    (export "[error-context-debug-message;encoding=utf8;realloc=cabi_realloc]" (func 9))
+    (export "[error-context-drop]" (func 10))
   )
-  (alias core export 0 "4" (core func (;8;)))
+  (alias core export 0 "4" (core func (;11;)))
   (core instance (;2;)
-    (export "[task-return]foo" (func 8))
+    (export "[task-return]foo" (func 11))
   )
   (core instance (;3;)
-    (export "[task-return]foo" (func 8))
+    (export "[task-return]foo" (func 11))
   )
   (core instance (;4;) (instantiate 0
       (with "$root" (instance 1))
@@ -129,31 +138,31 @@
   )
   (alias core export 4 "memory" (core memory (;0;)))
   (alias core export 0 "$imports" (core table (;0;)))
-  (core func (;9;) (canon task.wait (memory 0)))
-  (core func (;10;) (canon task.poll (memory 0)))
-  (core func (;11;) (canon error-context.new (memory 0) string-encoding=utf8))
-  (alias core export 4 "cabi_realloc" (core func (;12;)))
-  (core func (;13;) (canon error-context.debug-message (memory 0) (realloc 12) string-encoding=utf8))
-  (core func (;14;) (canon task.return (result string) (memory 0) string-encoding=utf8))
+  (core func (;12;) (canon waitable-set.wait (memory 0)))
+  (core func (;13;) (canon waitable-set.poll (memory 0)))
+  (core func (;14;) (canon error-context.new (memory 0) string-encoding=utf8))
+  (alias core export 4 "cabi_realloc" (core func (;15;)))
+  (core func (;16;) (canon error-context.debug-message (memory 0) (realloc 15) string-encoding=utf8))
+  (core func (;17;) (canon task.return (result string) (memory 0) string-encoding=utf8))
   (core instance (;5;)
     (export "$imports" (table 0))
-    (export "0" (func 9))
-    (export "1" (func 10))
-    (export "2" (func 11))
-    (export "3" (func 13))
-    (export "4" (func 14))
+    (export "0" (func 12))
+    (export "1" (func 13))
+    (export "2" (func 14))
+    (export "3" (func 16))
+    (export "4" (func 17))
   )
   (core instance (;6;) (instantiate 2
       (with "" (instance 5))
     )
   )
   (type (;0;) (func (param "s" string) (result string)))
-  (alias core export 4 "[async-stackful]foo" (core func (;15;)))
-  (func (;0;) (type 0) (canon lift (core func 15) (memory 0) (realloc 12) string-encoding=utf8 async))
+  (alias core export 4 "[async-stackful]foo" (core func (;18;)))
+  (func (;0;) (type 0) (canon lift (core func 18) (memory 0) (realloc 15) string-encoding=utf8 async))
   (export (;1;) "foo" (func 0))
   (type (;1;) (func (param "s" string) (result string)))
-  (alias core export 4 "[async-stackful]foo:foo/bar#foo" (core func (;16;)))
-  (func (;2;) (type 1) (canon lift (core func 16) (memory 0) (realloc 12) string-encoding=utf8 async))
+  (alias core export 4 "[async-stackful]foo:foo/bar#foo" (core func (;19;)))
+  (func (;2;) (type 1) (canon lift (core func 19) (memory 0) (realloc 15) string-encoding=utf8 async))
   (component (;0;)
     (type (;0;) (func (param "s" string) (result string)))
     (import "import-func-foo" (func (;0;) (type 0)))
diff --git a/crates/wit-component/tests/components/async-builtins/module.wat b/crates/wit-component/tests/components/async-builtins/module.wat
index 29ffb5eb..8c100741 100644
--- a/crates/wit-component/tests/components/async-builtins/module.wat
+++ b/crates/wit-component/tests/components/async-builtins/module.wat
@@ -2,8 +2,11 @@
   (import "$root" "[backpressure-set]" (func (param i32)))
   (import "[export]$root" "[task-return]foo" (func (param i32 i32)))
   (import "[export]foo:foo/bar" "[task-return]foo" (func (param i32 i32)))
-  (import "$root" "[task-wait]" (func (param i32) (result i32)))
-  (import "$root" "[task-poll]" (func (param i32) (result i32)))
+  (import "$root" "[waitable-set-new]" (func (result i32)))
+  (import "$root" "[waitable-set-wait]" (func (param i32 i32) (result i32)))
+  (import "$root" "[waitable-set-poll]" (func (param i32 i32) (result i32)))
+  (import "$root" "[waitable-set-drop]" (func (param i32)))
+  (import "$root" "[waitable-join]" (func (param i32 i32)))
   (import "$root" "[task-yield]" (func))
   (import "$root" "[subtask-drop]" (func (param i32)))
   (import "$root" "[error-context-new;encoding=utf8]" (func (param i32 i32) (result i32)))
diff --git a/tests/local/component-model-async/task-builtins.wast b/tests/local/component-model-async/task-builtins.wast
index f006e4e6..2066bd1a 100644
--- a/tests/local/component-model-async/task-builtins.wast
+++ b/tests/local/component-model-async/task-builtins.wast
@@ -59,54 +59,105 @@
   (core func (canon task.return (result u32) (realloc (func $i "r"))))
 )
 
-;; task.wait
+;; waitable-set.new
+(component
+  (core module $m (import "" "waitable-set.new" (func (result i32))))
+  (core func $waitable-set-new (canon waitable-set.new))
+  (core instance $i (instantiate $m (with "" (instance (export "waitable-set.new" (func $waitable-set-new))))))
+)
+
+;; waitable-set.new; incorrect type
+(assert_invalid
+  (component
+    (core module $m (import "" "waitable-set.new" (func (result i64))))
+    (core func $waitable-set-new (canon waitable-set.new))
+    (core instance $i (instantiate $m (with "" (instance (export "waitable-set.new" (func $waitable-set-new))))))
+  )
+  "type mismatch for export `waitable-set.new` of module instantiation argument ``"
+)
+
+;; waitable-set.wait
 (component
   (core module $libc (memory (export "memory") 1))
   (core instance $libc (instantiate $libc))
   (core module $m
-    (import "" "task.wait" (func $task-wait (param i32) (result i32)))
+    (import "" "waitable-set.wait" (func $waitable-set-wait (param i32 i32) (result i32)))
   )
-  (core func $task-wait (canon task.wait async (memory $libc "memory")))
-  (core instance $i (instantiate $m (with "" (instance (export "task.wait" (func $task-wait))))))
+  (core func $waitable-set-wait (canon waitable-set.wait async (memory $libc "memory")))
+  (core instance $i (instantiate $m (with "" (instance (export "waitable-set.wait" (func $waitable-set-wait))))))
 )
 
-;; task.wait; incorrect type
+;; waitable-set.wait; incorrect type
 (assert_invalid
   (component
     (core module $libc (memory (export "memory") 1))
     (core instance $libc (instantiate $libc))
     (core module $m
-      (import "" "task.wait" (func $task-wait (param i32 i32) (result i32)))
+      (import "" "waitable-set.wait" (func $waitable-set-wait (param i32) (result i32)))
     )
-    (core func $task-wait (canon task.wait async (memory $libc "memory")))
-    (core instance $i (instantiate $m (with "" (instance (export "task.wait" (func $task-wait))))))
+    (core func $waitable-set-wait (canon waitable-set.wait async (memory $libc "memory")))
+    (core instance $i (instantiate $m (with "" (instance (export "waitable-set.wait" (func $waitable-set-wait))))))
   )
-  "type mismatch for export `task.wait` of module instantiation argument ``"
+  "type mismatch for export `waitable-set.wait` of module instantiation argument ``"
 )
 
-;; task.poll
+;; waitable-set.poll
 (component
   (core module $libc (memory (export "memory") 1))
   (core instance $libc (instantiate $libc))
   (core module $m
-    (import "" "task.poll" (func $task-poll (param i32) (result i32)))
+    (import "" "waitable-set.poll" (func $waitable-set-poll (param i32 i32) (result i32)))
   )
-  (core func $task-poll (canon task.poll async (memory $libc "memory")))
-  (core instance $i (instantiate $m (with "" (instance (export "task.poll" (func $task-poll))))))
+  (core func $waitable-set-poll (canon waitable-set.poll async (memory $libc "memory")))
+  (core instance $i (instantiate $m (with "" (instance (export "waitable-set.poll" (func $waitable-set-poll))))))
 )
 
-;; task.poll; incorrect type
+;; waitable-set.poll; incorrect type
 (assert_invalid
   (component
     (core module $libc (memory (export "memory") 1))
     (core instance $libc (instantiate $libc))
     (core module $m
-      (import "" "task.poll" (func $task-poll (param i32 i32) (result i32)))
+      (import "" "waitable-set.poll" (func $waitable-set-poll (param i32) (result i32)))
     )
-    (core func $task-poll (canon task.poll async (memory $libc "memory")))
-    (core instance $i (instantiate $m (with "" (instance (export "task.poll" (func $task-poll))))))
+    (core func $waitable-set-poll (canon waitable-set.poll async (memory $libc "memory")))
+    (core instance $i (instantiate $m (with "" (instance (export "waitable-set.poll" (func $waitable-set-poll))))))
+  )
+  "type mismatch for export `waitable-set.poll` of module instantiation argument ``"
+)
+
+;; waitable-set.drop
+(component
+  (core module $m (import "" "waitable-set.drop" (func (param i32))))
+  (core func $waitable-set-drop (canon waitable-set.drop))
+  (core instance $i (instantiate $m (with "" (instance (export "waitable-set.drop" (func $waitable-set-drop))))))
+)
+
+;; waitable-set.drop; incorrect type
+(assert_invalid
+  (component
+    (core module $m (import "" "waitable-set.drop" (func (param i64))))
+    (core func $waitable-set-drop (canon waitable-set.drop))
+    (core instance $i (instantiate $m (with "" (instance (export "waitable-set.drop" (func $waitable-set-drop))))))
+  )
+  "type mismatch for export `waitable-set.drop` of module instantiation argument ``"
+)
+
+;; waitable.join
+(component
+  (core module $m (import "" "waitable.join" (func (param i32 i32))))
+  (core func $waitable.join (canon waitable.join))
+  (core instance $i (instantiate $m (with "" (instance (export "waitable.join" (func $waitable.join))))))
+)
+
+;; waitable.join; incorrect type
+(assert_invalid
+  (component
+    (core module $m (import "" "waitable.join" (func (param i64))))
+    (core func $waitable.join (canon waitable.join))
+    (core instance $i (instantiate $m (with "" (instance (export "waitable.join" (func $waitable.join))))))
   )
-  "type mismatch for export `task.poll` of module instantiation argument ``"
+  "type mismatch for export `waitable.join` of module instantiation argument ``"
 )
 
 ;; task.yield
diff --git a/tests/local/missing-features/component-model/async.wast b/tests/local/missing-features/component-model/async.wast
index f95ae5c0..5d0c51fe 100644
--- a/tests/local/missing-features/component-model/async.wast
+++ b/tests/local/missing-features/component-model/async.wast
@@ -52,32 +52,50 @@
   "`task.return` requires the component model async feature"
 )
 
-;; task.wait
+;; waitable-set.new
+(assert_invalid
+  (component (core func (canon waitable-set.new)))
+  "`waitable-set.new` requires the component model async feature"
+)
+
+;; waitable-set.wait
 (assert_invalid
   (component
     (core module $libc (memory (export "memory") 1))
     (core instance $libc (instantiate $libc))
     (core module $m
-      (import "" "task.wait" (func $task-wait (param i32) (result i32)))
+      (import "" "waitable-set.wait" (func $waitable-set-wait (param i32) (result i32)))
     )
-    (core func $task-wait (canon task.wait async (memory $libc "memory")))
-    (core instance $i (instantiate $m (with "" (instance (export "task.wait" (func $task-wait))))))
+    (core func $waitable-set-wait (canon waitable-set.wait async (memory $libc "memory")))
+    (core instance $i (instantiate $m (with "" (instance (export "waitable-set.wait" (func $waitable-set-wait))))))
   )
-  "`task.wait` requires the component model async feature"
+  "`waitable-set.wait` requires the component model async feature"
 )
 
-;; task.poll
+;; waitable-set.poll
 (assert_invalid
   (component
     (core module $libc (memory (export "memory") 1))
     (core instance $libc (instantiate $libc))
     (core module $m
-      (import "" "task.poll" (func $task-poll (param i32) (result i32)))
+      (import "" "waitable-set.poll" (func $waitable-set-poll (param i32) (result i32)))
     )
-    (core func $task-poll (canon task.poll async (memory $libc "memory")))
-    (core instance $i (instantiate $m (with "" (instance (export "task.poll" (func $task-poll))))))
+    (core func $waitable-set-poll (canon waitable-set.poll async (memory $libc "memory")))
+    (core instance $i (instantiate $m (with "" (instance (export "waitable-set.poll" (func $waitable-set-poll))))))
   )
-  "`task.poll` requires the component model async feature"
+  "`waitable-set.poll` requires the component model async feature"
+)
+
+;; waitable-set.drop
+(assert_invalid
+  (component (core func (canon waitable-set.drop)))
+  "`waitable-set.drop` requires the component model async feature"
+)
+
+;; waitable.join
+(assert_invalid
+  (component (core func (canon waitable.join)))
+  "`waitable.join` requires the component model async feature"
 )
 
 ;; task.yield
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast.json b/tests/snapshots/local/component-model-async/task-builtins.wast.json
index daabb0fe..5ee53942 100644
--- a/tests/snapshots/local/component-model-async/task-builtins.wast.json
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast.json
@@ -55,48 +55,87 @@
     },
     {
       "type": "assert_invalid",
-      "line": 75,
+      "line": 71,
       "filename": "task-builtins.8.wasm",
       "module_type": "binary",
-      "text": "type mismatch for export `task.wait` of module instantiation argument ``"
+      "text": "type mismatch for export `waitable-set.new` of module instantiation argument ``"
     },
     {
       "type": "module",
-      "line": 88,
+      "line": 80,
       "filename": "task-builtins.9.wasm",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 100,
+      "line": 92,
       "filename": "task-builtins.10.wasm",
       "module_type": "binary",
-      "text": "type mismatch for export `task.poll` of module instantiation argument ``"
+      "text": "type mismatch for export `waitable-set.wait` of module instantiation argument ``"
     },
     {
       "type": "module",
-      "line": 113,
+      "line": 105,
       "filename": "task-builtins.11.wasm",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 123,
+      "line": 117,
       "filename": "task-builtins.12.wasm",
       "module_type": "binary",
-      "text": "type mismatch for export `task.yield` of module instantiation argument ``"
+      "text": "type mismatch for export `waitable-set.poll` of module instantiation argument ``"
     },
     {
       "type": "module",
-      "line": 134,
+      "line": 130,
       "filename": "task-builtins.13.wasm",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 144,
+      "line": 138,
       "filename": "task-builtins.14.wasm",
       "module_type": "binary",
+      "text": "type mismatch for export `waitable-set.drop` of module instantiation argument ``"
+    },
+    {
+      "type": "module",
+      "line": 147,
+      "filename": "task-builtins.15.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 155,
+      "filename": "task-builtins.16.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for export `waitable.join` of module instantiation argument ``"
+    },
+    {
+      "type": "module",
+      "line": 164,
+      "filename": "task-builtins.17.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 174,
+      "filename": "task-builtins.18.wasm",
+      "module_type": "binary",
+      "text": "type mismatch for export `task.yield` of module instantiation argument ``"
+    },
+    {
+      "type": "module",
+      "line": 185,
+      "filename": "task-builtins.19.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 195,
+      "filename": "task-builtins.20.wasm",
+      "module_type": "binary",
       "text": "type mismatch for export `subtask.drop` of module instantiation argument ``"
     }
   ]
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/11.print b/tests/snapshots/local/component-model-async/task-builtins.wast/11.print
index 0851c2de..98bca2b0 100644
--- a/tests/snapshots/local/component-model-async/task-builtins.wast/11.print
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/11.print
@@ -1,14 +1,20 @@
 (component
-  (core module $m (;0;)
-    (type (;0;) (func))
-    (import "" "task.yield" (func $task-yield (;0;) (type 0)))
+  (core module $libc (;0;)
+    (memory (;0;) 1)
+    (export "memory" (memory 0))
   )
-  (core func $task-yield (;0;) (canon task.yield async))
-  (core instance (;0;)
-    (export "task.yield" (func $task-yield))
+  (core instance $libc (;0;) (instantiate $libc))
+  (core module $m (;1;)
+    (type (;0;) (func (param i32 i32) (result i32)))
+    (import "" "waitable-set.poll" (func $waitable-set-poll (;0;) (type 0)))
   )
-  (core instance $i (;1;) (instantiate $m
-      (with "" (instance 0))
+  (alias core export $libc "memory" (core memory (;0;)))
+  (core func $waitable-set-poll (;0;) (canon waitable-set.poll async (memory 0)))
+  (core instance (;1;)
+    (export "waitable-set.poll" (func $waitable-set-poll))
+  )
+  (core instance $i (;2;) (instantiate $m
+      (with "" (instance 1))
     )
   )
 )
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/13.print b/tests/snapshots/local/component-model-async/task-builtins.wast/13.print
index 038cc25c..8470c01e 100644
--- a/tests/snapshots/local/component-model-async/task-builtins.wast/13.print
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/13.print
@@ -1,11 +1,11 @@
 (component
   (core module $m (;0;)
     (type (;0;) (func (param i32)))
-    (import "" "subtask.drop" (func $subtask-drop (;0;) (type 0)))
+    (import "" "waitable-set.drop" (func (;0;) (type 0)))
   )
-  (core func $subtask-drop (;0;) (canon subtask.drop))
+  (core func $waitable-set-drop (;0;) (canon waitable-set.drop))
   (core instance (;0;)
-    (export "subtask.drop" (func $subtask-drop))
+    (export "waitable-set.drop" (func $waitable-set-drop))
   )
   (core instance $i (;1;) (instantiate $m
       (with "" (instance 0))
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/15.print b/tests/snapshots/local/component-model-async/task-builtins.wast/15.print
new file mode 100644
index 00000000..3667f8c3
--- /dev/null
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/15.print
@@ -0,0 +1,14 @@
+(component
+  (core module $m (;0;)
+    (type (;0;) (func (param i32 i32)))
+    (import "" "waitable.join" (func (;0;) (type 0)))
+  )
+  (core func $waitable.join (;0;) (canon waitable.join))
+  (core instance (;0;)
+    (export "waitable.join" (func $waitable.join))
+  )
+  (core instance $i (;1;) (instantiate $m
+      (with "" (instance 0))
+    )
+  )
+)
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/17.print b/tests/snapshots/local/component-model-async/task-builtins.wast/17.print
new file mode 100644
index 00000000..0851c2de
--- /dev/null
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/17.print
@@ -0,0 +1,14 @@
+(component
+  (core module $m (;0;)
+    (type (;0;) (func))
+    (import "" "task.yield" (func $task-yield (;0;) (type 0)))
+  )
+  (core func $task-yield (;0;) (canon task.yield async))
+  (core instance (;0;)
+    (export "task.yield" (func $task-yield))
+  )
+  (core instance $i (;1;) (instantiate $m
+      (with "" (instance 0))
+    )
+  )
+)
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/19.print b/tests/snapshots/local/component-model-async/task-builtins.wast/19.print
new file mode 100644
index 00000000..038cc25c
--- /dev/null
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/19.print
@@ -0,0 +1,14 @@
+(component
+  (core module $m (;0;)
+    (type (;0;) (func (param i32)))
+    (import "" "subtask.drop" (func $subtask-drop (;0;) (type 0)))
+  )
+  (core func $subtask-drop (;0;) (canon subtask.drop))
+  (core instance (;0;)
+    (export "subtask.drop" (func $subtask-drop))
+  )
+  (core instance $i (;1;) (instantiate $m
+      (with "" (instance 0))
+    )
+  )
+)
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/7.print b/tests/snapshots/local/component-model-async/task-builtins.wast/7.print
index 05c40763..967b3b8c 100644
--- a/tests/snapshots/local/component-model-async/task-builtins.wast/7.print
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/7.print
@@ -1,20 +1,14 @@
 (component
-  (core module $libc (;0;)
-    (memory (;0;) 1)
-    (export "memory" (memory 0))
+  (core module $m (;0;)
+    (type (;0;) (func (result i32)))
+    (import "" "waitable-set.new" (func (;0;) (type 0)))
   )
-  (core instance $libc (;0;) (instantiate $libc))
-  (core module $m (;1;)
-    (type (;0;) (func (param i32) (result i32)))
-    (import "" "task.wait" (func $task-wait (;0;) (type 0)))
+  (core func $waitable-set-new (;0;) (canon waitable-set.new))
+  (core instance (;0;)
+    (export "waitable-set.new" (func $waitable-set-new))
   )
-  (alias core export $libc "memory" (core memory (;0;)))
-  (core func $task-wait (;0;) (canon task.wait async (memory 0)))
-  (core instance (;1;)
-    (export "task.wait" (func $task-wait))
-  )
-  (core instance $i (;2;) (instantiate $m
-      (with "" (instance 1))
+  (core instance $i (;1;) (instantiate $m
+      (with "" (instance 0))
     )
   )
 )
diff --git a/tests/snapshots/local/component-model-async/task-builtins.wast/9.print b/tests/snapshots/local/component-model-async/task-builtins.wast/9.print
index 05bfe3aa..8835e4a1 100644
--- a/tests/snapshots/local/component-model-async/task-builtins.wast/9.print
+++ b/tests/snapshots/local/component-model-async/task-builtins.wast/9.print
@@ -5,13 +5,13 @@
   )
   (core instance $libc (;0;) (instantiate $libc))
   (core module $m (;1;)
-    (type (;0;) (func (param i32) (result i32)))
-    (import "" "task.poll" (func $task-poll (;0;) (type 0)))
+    (type (;0;) (func (param i32 i32) (result i32)))
+    (import "" "waitable-set.wait" (func $waitable-set-wait (;0;) (type 0)))
   )
   (alias core export $libc "memory" (core memory (;0;)))
-  (core func $task-poll (;0;) (canon task.poll async (memory 0)))
+  (core func $waitable-set-wait (;0;) (canon waitable-set.wait async (memory 0)))
   (core instance (;1;)
-    (export "task.poll" (func $task-poll))
+    (export "waitable-set.wait" (func $waitable-set-wait))
   )
   (core instance $i (;2;) (instantiate $m
       (with "" (instance 1))
diff --git a/tests/snapshots/local/missing-features/component-model/async.wast.json b/tests/snapshots/local/missing-features/component-model/async.wast.json
index 97490cf0..b80ec6bf 100644
--- a/tests/snapshots/local/missing-features/component-model/async.wast.json
+++ b/tests/snapshots/local/missing-features/component-model/async.wast.json
@@ -34,175 +34,196 @@
       "line": 57,
       "filename": "async.4.wasm",
       "module_type": "binary",
-      "text": "`task.wait` requires the component model async feature"
+      "text": "`waitable-set.new` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 71,
+      "line": 63,
       "filename": "async.5.wasm",
       "module_type": "binary",
-      "text": "`task.poll` requires the component model async feature"
+      "text": "`waitable-set.wait` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 85,
+      "line": 77,
       "filename": "async.6.wasm",
       "module_type": "binary",
-      "text": "`task.yield` requires the component model async feature"
+      "text": "`waitable-set.poll` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 97,
+      "line": 91,
       "filename": "async.7.wasm",
       "module_type": "binary",
-      "text": "`subtask.drop` requires the component model async feature"
+      "text": "`waitable-set.drop` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 109,
+      "line": 97,
       "filename": "async.8.wasm",
       "module_type": "binary",
-      "text": "requires the component model async feature"
+      "text": "`waitable.join` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 122,
+      "line": 103,
       "filename": "async.9.wasm",
       "module_type": "binary",
-      "text": "requires the component model async feature"
+      "text": "`task.yield` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 137,
+      "line": 115,
       "filename": "async.10.wasm",
       "module_type": "binary",
-      "text": "requires the component model async feature"
+      "text": "`subtask.drop` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 152,
+      "line": 127,
       "filename": "async.11.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 165,
+      "line": 140,
       "filename": "async.12.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 178,
+      "line": 155,
       "filename": "async.13.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 191,
+      "line": 170,
       "filename": "async.14.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 204,
+      "line": 183,
       "filename": "async.15.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 217,
+      "line": 196,
       "filename": "async.16.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 232,
+      "line": 209,
       "filename": "async.17.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 247,
+      "line": 222,
       "filename": "async.18.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 260,
+      "line": 235,
       "filename": "async.19.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 273,
+      "line": 250,
       "filename": "async.20.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 286,
+      "line": 265,
       "filename": "async.21.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 299,
+      "line": 278,
       "filename": "async.22.wasm",
       "module_type": "binary",
-      "text": "`error-context.new` requires the component model async feature"
+      "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 313,
+      "line": 291,
       "filename": "async.23.wasm",
       "module_type": "binary",
-      "text": "`error-context.debug-message` requires the component model async feature"
+      "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 330,
+      "line": 304,
       "filename": "async.24.wasm",
       "module_type": "binary",
-      "text": "`error-context.drop` requires the component model async feature"
+      "text": "requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 342,
+      "line": 317,
       "filename": "async.25.wasm",
       "module_type": "binary",
-      "text": "requires the component model async feature"
+      "text": "`error-context.new` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 346,
+      "line": 331,
       "filename": "async.26.wasm",
       "module_type": "binary",
-      "text": "requires the component model async feature"
+      "text": "`error-context.debug-message` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 350,
+      "line": 348,
       "filename": "async.27.wasm",
       "module_type": "binary",
-      "text": "requires the component model async feature"
+      "text": "`error-context.drop` requires the component model async feature"
     },
     {
       "type": "assert_invalid",
-      "line": 354,
+      "line": 360,
       "filename": "async.28.wasm",
       "module_type": "binary",
       "text": "requires the component model async feature"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 364,
+      "filename": "async.29.wasm",
+      "module_type": "binary",
+      "text": "requires the component model async feature"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 368,
+      "filename": "async.30.wasm",
+      "module_type": "binary",
+      "text": "requires the component model async feature"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 372,
+      "filename": "async.31.wasm",
+      "module_type": "binary",
+      "text": "requires the component model async feature"
     }
   ]
 }
\ No newline at end of file