From b38aa77e6aa5cbb843082887f54ed58c6272bd13 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 2 Apr 2025 11:23:25 -0700 Subject: [PATCH 1/6] Implement a C ABI for async import/export communication This commit is an implementation of a solution for WebAssembly/component-model#485 for Rust. This should enable releasing multiple versions of `wit-bindgen` into the wild and have them all work together for now. Integration with `wasi-libc` will come in the future in theory. --- ...lloc.sh => rebuild-libwit-bindgen-cabi.sh} | 48 +++++--- crates/guest-rust/rt/README.md | 10 +- crates/guest-rust/rt/build.rs | 4 +- crates/guest-rust/rt/src/async_support.rs | 111 ++++++++++++------ .../guest-rust/rt/src/async_support/cabi.rs | 105 +++++++++++++++++ .../rt/src/async_support/waitable.rs | 79 +++++++++++-- crates/guest-rust/rt/src/cabi_realloc.o | Bin 261 -> 0 bytes crates/guest-rust/rt/src/cabi_realloc.rs | 11 -- crates/guest-rust/rt/src/lib.rs | 8 +- .../guest-rust/rt/src/libwit_bindgen_cabi.a | Bin 0 -> 628 bytes .../rt/src/libwit_bindgen_cabi_realloc.a | Bin 412 -> 0 bytes .../{cabi_realloc.c => wit_bindgen_cabi.c} | 9 +- crates/guest-rust/rt/src/wit_bindgen_cabi.o | Bin 0 -> 378 bytes crates/guest-rust/rt/src/wit_bindgen_cabi.rs | 22 ++++ 14 files changed, 321 insertions(+), 86 deletions(-) rename ci/{rebuild-libcabi-realloc.sh => rebuild-libwit-bindgen-cabi.sh} (69%) create mode 100644 crates/guest-rust/rt/src/async_support/cabi.rs delete mode 100644 crates/guest-rust/rt/src/cabi_realloc.o delete mode 100644 crates/guest-rust/rt/src/cabi_realloc.rs create mode 100644 crates/guest-rust/rt/src/libwit_bindgen_cabi.a delete mode 100644 crates/guest-rust/rt/src/libwit_bindgen_cabi_realloc.a rename crates/guest-rust/rt/src/{cabi_realloc.c => wit_bindgen_cabi.c} (58%) create mode 100644 crates/guest-rust/rt/src/wit_bindgen_cabi.o create mode 100644 crates/guest-rust/rt/src/wit_bindgen_cabi.rs diff --git a/ci/rebuild-libcabi-realloc.sh b/ci/rebuild-libwit-bindgen-cabi.sh similarity index 69% rename from ci/rebuild-libcabi-realloc.sh rename to ci/rebuild-libwit-bindgen-cabi.sh index 0d76fa947..1a903c06b 100755 --- a/ci/rebuild-libcabi-realloc.sh +++ b/ci/rebuild-libwit-bindgen-cabi.sh @@ -41,13 +41,15 @@ set -ex version=$(./ci/print-current-version.sh | sed 's/\./_/g') -sym=cabi_realloc_wit_bindgen_$version +realloc=cabi_realloc_wit_bindgen_$version +wasip3_task_set=wasip3_task_set_wit_bindgen_$version +wasip3_task_get=wasip3_task_get_wit_bindgen_$version -cat >./crates/guest-rust/rt/src/cabi_realloc.rs <<-EOF +cat >./crates/guest-rust/rt/src/wit_bindgen_cabi.rs <<-EOF // This file is generated by $0 #[unsafe(no_mangle)] -pub unsafe extern "C" fn $sym( +pub unsafe extern "C" fn $realloc( old_ptr: *mut u8, old_len: usize, align: usize, @@ -55,31 +57,49 @@ pub unsafe extern "C" fn $sym( ) -> *mut u8 { crate::cabi_realloc(old_ptr, old_len, align, new_len) } + +static mut WASIP3_TASK: *mut u8 = core::ptr::null_mut(); + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn $wasip3_task_set(ptr: *mut u8) -> *mut u8 { + unsafe { + let ret = WASIP3_TASK; + WASIP3_TASK = ptr; + ret + } +} EOF -cat >./crates/guest-rust/rt/src/cabi_realloc.c <<-EOF +cat >./crates/guest-rust/rt/src/wit_bindgen_cabi.c <<-EOF // This file is generated by $0 #include -extern void *$sym(void *ptr, size_t old_size, size_t align, size_t new_size); +extern void *$realloc(void *ptr, size_t old_size, size_t align, size_t new_size); __attribute__((__weak__, __export_name__("cabi_realloc"))) void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { - return $sym(ptr, old_size, align, new_size); + return $realloc(ptr, old_size, align, new_size); +} + +extern void *$wasip3_task_set(void *ptr); + +__attribute__((__weak__)) +void *wasip3_task_set(void *ptr) { + return $wasip3_task_set(ptr); } EOF -rm -f crates/guest-rust/rt/src/cabi_realloc.o -$WASI_SDK_PATH/bin/clang crates/guest-rust/rt/src/cabi_realloc.c \ - -O -c -o crates/guest-rust/rt/src/cabi_realloc.o +rm -f crates/guest-rust/rt/src/wit_bindgen_cabi.o +$WASI_SDK_PATH/bin/clang crates/guest-rust/rt/src/wit_bindgen_cabi.c \ + -O -c -o crates/guest-rust/rt/src/wit_bindgen_cabi.o # Remove the `producers` section. This appears to differ whether the host for # clang is either macOS or Linux. Not needed here anyway, so discard it to help # either host produce the same object. -wasm-tools strip -d producers ./crates/guest-rust/rt/src/cabi_realloc.o \ - -o ./crates/guest-rust/rt/src/cabi_realloc.o +wasm-tools strip -d producers ./crates/guest-rust/rt/src/wit_bindgen_cabi.o \ + -o ./crates/guest-rust/rt/src/wit_bindgen_cabi.o -rm -f crates/guest-rust/rt/src/libwit_bindgen_cabi_realloc.a -$WASI_SDK_PATH/bin/llvm-ar crus crates/guest-rust/rt/src/libwit_bindgen_cabi_realloc.a \ - crates/guest-rust/rt/src/cabi_realloc.o +rm -f crates/guest-rust/rt/src/libwit_bindgen_cabi.a +$WASI_SDK_PATH/bin/llvm-ar crus crates/guest-rust/rt/src/libwit_bindgen_cabi.a \ + crates/guest-rust/rt/src/wit_bindgen_cabi.o diff --git a/crates/guest-rust/rt/README.md b/crates/guest-rust/rt/README.md index 2c9c8b2cc..c2824b04c 100644 --- a/crates/guest-rust/rt/README.md +++ b/crates/guest-rust/rt/README.md @@ -8,13 +8,13 @@ crate is to contain "runtime" code related to the macro-expansion of the be removed in some situations. This crate contains a precompiled object file and archive at -`src/cabi_realloc.o` and `src/libwit_bindgen_cabi_realloc.a`. This is compiled -from the source `src/cabi_realloc.c` and is checked in as precompiled to avoid -needing a C compiler at compile-time which isn't always available. This object -file is only used on wasm targets. +`src/wit_bindgen_cabi.o` and `src/libwit_bindgen_cabi.a`. This is compiled +from the source `src/wit_bindgen_cabi.c` and is checked in as precompiled to +avoid needing a C compiler at compile-time which isn't always available. This +object file is only used on wasm targets. The object file is compiled by -[this script]https://github.com/bytecodealliance/wit-bindgen/blob/main/ci/rebuild-libcabi-realloc.sh) +[this script]https://github.com/bytecodealliance/wit-bindgen/blob/main/ci/rebuild-libwit-bindgen-cabi.sh) and is verified in repository continuous integration that the checked-in versions match what CI produces. diff --git a/crates/guest-rust/rt/build.rs b/crates/guest-rust/rt/build.rs index 4bfbed9e0..c8c95090b 100644 --- a/crates/guest-rust/rt/build.rs +++ b/crates/guest-rust/rt/build.rs @@ -16,10 +16,10 @@ fn main() { let mut src = env::current_dir().unwrap(); src.push("src"); - src.push("libwit_bindgen_cabi_realloc.a"); + src.push("libwit_bindgen_cabi.a"); let dst_name = format!( - "wit_bindgen_cabi_realloc{}", + "wit_bindgen_cabi{}", env!("CARGO_PKG_VERSION").replace(".", "_") ); let dst = out_dir.join(format!("lib{dst_name}.a")); diff --git a/crates/guest-rust/rt/src/async_support.rs b/crates/guest-rust/rt/src/async_support.rs index 5a9f25fd0..5749e3364 100644 --- a/crates/guest-rust/rt/src/async_support.rs +++ b/crates/guest-rust/rt/src/async_support.rs @@ -8,6 +8,7 @@ extern crate std; use core::sync::atomic::{AtomicBool, Ordering}; use std::boxed::Box; use std::collections::HashMap; +use std::ffi::c_void; use std::fmt::{self, Debug, Display}; use std::future::Future; use std::mem; @@ -15,7 +16,7 @@ use std::pin::Pin; use std::ptr; use std::string::String; use std::sync::Arc; -use std::task::{Context, Poll, Wake, Waker}; +use std::task::{Context, Poll, Wake}; use std::vec::Vec; use futures::channel::oneshot; @@ -36,6 +37,7 @@ macro_rules! rtdebug { } mod abi_buffer; +mod cabi; mod future_support; mod stream_support; mod waitable; @@ -60,21 +62,32 @@ struct FutureState { tasks: Option>, /// The waitable set containing waitables created by this task, if any. waitable_set: Option, - /// A map of waitables to the corresponding waker and completion code. - /// - /// This is primarily filled in and managed by `WaitableOperation`. The - /// waker here comes straight from `std::task::Context` and the pointer is - /// otherwise stored within the `WaitableOperation` The raw pointer here - /// has a disconnected lifetime with each future but the management of the - /// internal states with respect to drop should always ensure that this is - /// only ever pointing to active waitable operations. - /// - /// When a waitable notification is received the corresponding entry in this - /// map is removed, the status code is filled in, and the waker is notified. - wakers: HashMap)>, + + /// State of all waitables in `waitable_set`, and the ptr/callback they're + /// associated with. + waitables: HashMap, + + /// Raw structure used to pass to `cabi::wasip3_task_set` + wasip3_task: cabi::wasip3_task, } impl FutureState { + fn new(future: BoxFuture) -> FutureState { + FutureState { + todo: 0, + tasks: Some([future].into_iter().collect()), + waitable_set: None, + waitables: HashMap::new(), + wasip3_task: cabi::wasip3_task { + // This pointer is filled in before calling `wasip3_task_set`. + ptr: ptr::null_mut(), + version: cabi::WASIP3_TASK_V1, + waitable_register, + waitable_unregister, + }, + } + } + fn get_or_create_waitable_set(&mut self) -> u32 { *self.waitable_set.get_or_insert_with(waitable_set_new) } @@ -88,7 +101,32 @@ impl FutureState { } fn remaining_work(&self) -> bool { - self.todo > 0 || !self.wakers.is_empty() + self.todo > 0 || !self.waitables.is_empty() + } +} + +unsafe extern "C" fn waitable_register( + ptr: *mut c_void, + waitable: u32, + callback: unsafe extern "C" fn(*mut c_void, u32), + callback_ptr: *mut c_void, +) -> *mut c_void { + let ptr = ptr.cast::(); + assert!(!ptr.is_null()); + (*ptr).add_waitable(waitable); + match (*ptr).waitables.insert(waitable, (callback_ptr, callback)) { + Some((prev, _)) => prev, + None => ptr::null_mut(), + } +} + +unsafe extern "C" fn waitable_unregister(ptr: *mut c_void, waitable: u32) -> *mut c_void { + let ptr = ptr.cast::(); + assert!(!ptr.is_null()); + (*ptr).remove_waitable(waitable); + match (*ptr).waitables.remove(&waitable) { + Some((prev, _)) => prev, + None => ptr::null_mut(), } } @@ -145,6 +183,22 @@ unsafe fn poll(state: *mut FutureState) -> Poll<()> { } } + // Finish our `wasip3_task` by initializing its self-referential pointer, + // and then register it for the duration of this function with + // `wasip3_task_set`. The previous value of `wasip3_task_set` will get + // restored when this function returns. + struct ResetTask(*mut cabi::wasip3_task); + impl Drop for ResetTask { + fn drop(&mut self) { + unsafe { + cabi::wasip3_task_set(self.0); + } + } + } + (*state).wasip3_task.ptr = state.cast(); + let prev = cabi::wasip3_task_set(&mut (*state).wasip3_task); + let _reset = ResetTask(prev); + loop { if let Some(futures) = (*state).tasks.as_mut() { let old = CURRENT; @@ -191,16 +245,9 @@ pub fn first_poll( future: impl Future + 'static, fun: impl FnOnce(&T) + 'static, ) -> i32 { - let state = Box::into_raw(Box::new(FutureState { - todo: 0, - tasks: Some( - [Box::pin(future.map(|v| fun(&v))) as BoxFuture] - .into_iter() - .collect(), - ), - waitable_set: None, - wakers: HashMap::new(), - })); + let state = Box::into_raw(Box::new(FutureState::new(Box::pin( + future.map(|v| fun(&v)), + )))); let done = unsafe { poll(state).is_ready() }; unsafe { callback_code(state, done) } } @@ -339,9 +386,8 @@ unsafe fn callback_with_state( "EVENT_{{STREAM,FUTURE}}_{{READ,WRITE}}({event0:#x}, {event1:#x}, {event2:#x})" ); (*state).remove_waitable(event1 as u32); - let (waker, code) = (*state).wakers.remove(&(event1 as u32)).unwrap(); - *code = Some(event2 as u32); - waker.wake(); + let (ptr, callback) = (*state).waitables.remove(&(event1 as u32)).unwrap(); + callback(ptr, event2 as u32); let done = poll(state).is_ready(); callback_code(state, done) @@ -469,16 +515,7 @@ pub fn spawn(future: impl Future + 'static) { // TODO: refactor so `'static` bounds aren't necessary pub fn block_on(future: impl Future + 'static) -> T { let (tx, mut rx) = oneshot::channel(); - let state = &mut FutureState { - todo: 0, - tasks: Some( - [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] - .into_iter() - .collect(), - ), - waitable_set: None, - wakers: HashMap::new(), - }; + let state = &mut FutureState::new(Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture); loop { match unsafe { poll(state) } { Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), diff --git a/crates/guest-rust/rt/src/async_support/cabi.rs b/crates/guest-rust/rt/src/async_support/cabi.rs new file mode 100644 index 000000000..59958da8c --- /dev/null +++ b/crates/guest-rust/rt/src/async_support/cabi.rs @@ -0,0 +1,105 @@ +//! Definition of the "C ABI" of how imported functions interact with exported +//! tasks. +//! +//! Ok this crate is written in Rust, why in the world does this exist? This +//! comment is intended to explain this rationale but the tl;dr; is we want +//! this to work: +//! +//! * Within a single component ... +//! * One rust crate uses `wit-bindgen 0.A.0` to generate an exported function. +//! * One rust crate uses `wit-bindgen 0.B.0` to bind an imported function. +//! * The two crates are connected in the application with +//! `std::future::Future`. +//! +//! Without this module this situation won't work because 0.A.0 has no +//! knowledge of 0.B.0 meaning that when 0.B.0 decides to block it won't know +//! where to register its `waitable` within a `waitable-set`. +//! +//! To solve this problem the long-term intention is that something will live +//! in `wasi-libc` itself, but in the meantime it's living "somewhere" within +//! `wit-bindgen 0.*.0`. Specifically all `wit-bindgen` versions will all +//! reference, via C linkage, a single function which is used to manipulate a +//! single pointer in linear memory. This pointer is a `wasip3_task` structure +//! which has all the various fields to use it. +//! +//! The `wasip3_task_set` symbol is itself defined in C inside of the +//! `src/wit_bindgen_cabi.c` file at this time, specifically because it's +//! annotated with `__weak__` meaning that any definition of it suffices. This +//! isn't possible to define in stable Rust (specifically `__weak__`). +//! +//! Once `wasip3_task_set` is defined everything then operates via indirection, +//! aka based off the returned pointer. The intention is that exported functions +//! will set this (it's sort of like an executor) and then imported functions +//! will all use this as the source of registering waitables. In the end that +//! means that it's possible to share types with `std::future::Future` that +//! are backed at the ABI level with this "channel". +//! +//! In the future it's hoped that this can move into `wasi-libc` itself, or if +//! `wasi-libc` provides something else that would be prioritized over this. +//! For now this is basically an affordance that we're going to be frequently +//! releaseing new major versions of `wit-bindgen` and we don't want to force +//! applications to all be using the exact same version of the bindings +//! generator and async bindings. +//! +//! Additionally for now this file is serving as documentation of this +//! interface. + +use core::ffi::c_void; + +extern "C" { + /// Sets the global task pointer to `ptr` provided. Returns the previous + /// value. + /// + /// This function acts as both a dual getter and a setter. To get the + /// current task pointer a dummy `ptr` can be provided (e.g. NULL) and then + /// it's passed back when you're done working with it. When setting the + /// current task pointer it's recommended to call this and then call it + /// again with the previous value when the tasks's work is done. + /// + /// For executors they need to ensure that the `ptr` passed in lives for + /// the entire lifetime of the component model task. + pub fn wasip3_task_set(ptr: *mut wasip3_task) -> *mut wasip3_task; +} + +/// The first version of `wasip3_task` which implies the existence of the +/// fields `ptr`, `waitable_register`, and `waitable_unregister`. +pub const WASIP3_TASK_V1: u32 = 1; + +/// Indirect "vtable" used to connect imported functions and exported tasks. +/// Executors (e.g. exported functions) define and manage this while imports +/// use it. +#[repr(C)] +pub struct wasip3_task { + /// Currently `WASIP3_TASK_V1`. Indicates what fields are present next + /// depending on the version here. + pub version: u32, + + /// Private pointer owned by the `wasip3_task` itself, passed to callbacks + /// below as the first argument. + pub ptr: *mut c_void, + + /// Register a new `waitable` for this exported task. + /// + /// This exported task will add `waitable` to its `waitable-set`. When it + /// becomes ready then `callback` will be invoked with the ready code as + /// well as the `callback_ptr` provided. + /// + /// If `waitable` was previously registered with this task then the + /// previuos `callback_ptr` is returned. Otherwise `NULL` is returned. + /// + /// It's the caller's responsibility to ensure that `callback_ptr` is valid + /// until `callback` is invoked, `waitable_unregister` is invoked, or + /// `waitable_register` is called again to overwrite the value. + pub waitable_register: unsafe extern "C" fn( + ptr: *mut c_void, + waitable: u32, + callback: unsafe extern "C" fn(callback_ptr: *mut c_void, code: u32), + callback_ptr: *mut c_void, + ) -> *mut c_void, + + /// Removes the `waitable` from this task's `waitable-set`. + /// + /// Returns the `callback_ptr` passed to `waitable_register` if present, or + /// `NULL` if it's not present. + pub waitable_unregister: unsafe extern "C" fn(ptr: *mut c_void, waitable: u32) -> *mut c_void, +} diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 956fc46aa..2f7dd235d 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -1,11 +1,13 @@ //! Generic support for "any waitable" and performing asynchronous operations on //! that waitable. -use super::results; +use super::{cabi, results}; +use std::ffi::c_void; use std::marker; use std::mem; use std::pin::Pin; -use std::task::{Context, Poll}; +use std::ptr; +use std::task::{Context, Poll, Waker}; /// Generic future-based operation on any "waitable" in the component model. /// @@ -42,6 +44,8 @@ struct CompletionStatus { /// happens. code: Option, + waker: Option, + /// This is necessary to ensure that `Pin<&mut CompletionStatus>` carries /// the "pin guarantee", basically to mean that it's not safe to construct /// `Pin<&mut CompletionStatus>` and it must somehow require `unsafe` code. @@ -130,6 +134,7 @@ where state: WaitableOperationState::Start(state), completion_status: CompletionStatus { code: None, + waker: None, _pinned: marker::PhantomPinned, }, } @@ -154,12 +159,38 @@ where /// * Fill in `completion_status` with the result of a completion event. /// * Call `cx.waker().wake()`. pub fn register_waker(self: Pin<&mut Self>, waitable: u32, cx: &mut Context) { - let (_, completion_status) = self.pin_project(); + let (_, mut completion_status) = self.pin_project(); + debug_assert!(completion_status.as_mut().code_mut().is_none()); + *completion_status.as_mut().waker_mut() = Some(cx.waker().clone()); + + // SAFETY: There's quite a lot going on here. First is the usage of + // `task` below, and for that see `unregister_waker` below for why this + // pattern should be safe. + // + // Otherwise we're handing off a pointer to `completion_status` to the + // `task` itself. That should be safe as we're guaranteed, via + // `Pin<&mut Self>`, that before `&mut Self` is deallocated the + // destructor will be run which will perform de-registration via + // cancellation. unsafe { - (*super::CURRENT).add_waitable(waitable); - (*super::CURRENT) - .wakers - .insert(waitable, (cx.waker().clone(), completion_status.code_mut())); + let task = cabi::wasip3_task_set(ptr::null_mut()); + assert!(!task.is_null()); + assert!((*task).version >= cabi::WASIP3_TASK_V1); + let ptr: *mut CompletionStatus = completion_status.get_unchecked_mut(); + let prev = ((*task).waitable_register)((*task).ptr, waitable, cabi_wake, ptr.cast()); + // We might be inserting a waker for the first time or overwriting + // the previous waker. Only assert the expected value here if the + // previous value was non-null. + if !prev.is_null() { + assert_eq!(ptr, prev.cast()); + } + cabi::wasip3_task_set(task); + } + + unsafe extern "C" fn cabi_wake(ptr: *mut c_void, code: u32) { + let ptr: &mut CompletionStatus = &mut *ptr.cast::(); + ptr.code = Some(code); + ptr.waker.take().unwrap().wake() } } @@ -169,17 +200,37 @@ where /// This relinquishes control of the original `completion_status` pointer /// passed to `register_waker` after this call has completed. pub fn unregister_waker(self: Pin<&mut Self>, waitable: u32) { + // SAFETY: the contract of `wasip3_task_set` is that the returned + // pointer is valid for the lifetime of our entire task, so it's valid + // for this stack frame. Additionally we assert it's non-null to + // double-check it's initialized and additionlly check the version for + // the fields that we access. + // + // Otherwise the `waitable_unregister` callback should be safe if we + // pass a waitable that we own and the task's own pointer. unsafe { - (*super::CURRENT).remove_waitable(waitable); - let _prev = (*super::CURRENT).wakers.remove(&waitable); - // Note that `_prev` here is not guaranteed to be either `Some` or - // `None`. A racy completion notification may have come in and + let task = cabi::wasip3_task_set(ptr::null_mut()); + assert!(!task.is_null()); + assert!((*task).version >= cabi::WASIP3_TASK_V1); + let prev = ((*task).waitable_unregister)((*task).ptr, waitable); + + // Note that `_prev` here is not guaranteed to be either `NULL` or + // not. A racy completion notification may have come in and // removed our waitable from the map even though we're in the // `InProgress` state, meaning it may not be present. // // The main thing is that after this method is called the // internal `completion_status` is guaranteed to no longer be in - // `FuturesState`. + // `task`. + // + // Note, though, that if present this must be our `CompletionStatus` + // pointer. + if !prev.is_null() { + let ptr: *mut CompletionStatus = self.pin_project().1.get_unchecked_mut(); + assert_eq!(ptr, prev.cast()); + } + + cabi::wasip3_task_set(task); } } @@ -374,4 +425,8 @@ impl CompletionStatus { fn code_mut(self: Pin<&mut Self>) -> &mut Option { unsafe { &mut self.get_unchecked_mut().code } } + + fn waker_mut(self: Pin<&mut Self>) -> &mut Option { + unsafe { &mut self.get_unchecked_mut().waker } + } } diff --git a/crates/guest-rust/rt/src/cabi_realloc.o b/crates/guest-rust/rt/src/cabi_realloc.o deleted file mode 100644 index 3ce8afc4655bba1c34668a23282ec25d820a93dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 261 zcmZ9GK~BRk5Jmqum0CijSnOCe3wEWbNZbIr<_J?K43>rCAjfH8RW891xMyA1p#J64 z|1%F}=m-E^&~|djp@Z5Mg@j$j>|GEOxs$Gsp9(nPMQa+zQH=L}%X^o&apA>E;59$o z^ZKuK&77%=(k6Z#Lz`S5IGM%^z2}A^Of}8_p8}Ovq*0@$TGfUA%E2XWrdi_61*>M? zC!+8}XG0C>GD_CDdwhP{ObBzrZIw*4lD~^dW0XNHt8V-?SXtjU#t+Nc;A~jQM>+y= Ck4ta> diff --git a/crates/guest-rust/rt/src/cabi_realloc.rs b/crates/guest-rust/rt/src/cabi_realloc.rs deleted file mode 100644 index cc738191e..000000000 --- a/crates/guest-rust/rt/src/cabi_realloc.rs +++ /dev/null @@ -1,11 +0,0 @@ -// This file is generated by ./ci/rebuild-libcabi-realloc.sh - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_41_0( - old_ptr: *mut u8, - old_len: usize, - align: usize, - new_len: usize, -) -> *mut u8 { - crate::cabi_realloc(old_ptr, old_len, align, new_len) -} diff --git a/crates/guest-rust/rt/src/lib.rs b/crates/guest-rust/rt/src/lib.rs index 31b60afb0..608d04846 100644 --- a/crates/guest-rust/rt/src/lib.rs +++ b/crates/guest-rust/rt/src/lib.rs @@ -13,9 +13,9 @@ use core::ptr::{self, NonNull}; #[doc(hidden)] pub use bitflags; -/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`. +/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`. #[cfg(not(target_env = "p2"))] -mod cabi_realloc; +mod wit_bindgen_cabi; /// This function is called from generated bindings and will be deleted by /// the linker. The purpose of this function is to force a reference to the @@ -23,7 +23,7 @@ mod cabi_realloc; /// command line. That way `wasm-ld` will pick it up, see it needs to be /// exported, and then export it. /// -/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`. +/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`. pub fn maybe_link_cabi_realloc() { #[cfg(all(target_family = "wasm", not(target_env = "p2")))] { @@ -55,7 +55,7 @@ pub fn maybe_link_cabi_realloc() { /// NB: this function is called by a generated function in the /// `cabi_realloc` module above. It's otherwise never explicitly called. /// -/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`. +/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`. #[cfg(not(target_env = "p2"))] pub unsafe fn cabi_realloc( old_ptr: *mut u8, diff --git a/crates/guest-rust/rt/src/libwit_bindgen_cabi.a b/crates/guest-rust/rt/src/libwit_bindgen_cabi.a new file mode 100644 index 0000000000000000000000000000000000000000..afac3d661f9bd64b0d549812be0aeadf30b22c80 GIT binary patch literal 628 zcmZ{hy-ve05Xa9>DymSCNDPPp5m=*s&{n{Jx&Q+ckKj7t8muNxr~v;y}J8;zq?KtIsib;`Yu<*L%|gCB*`L} z@mx%LO!Iuqa?}9r-7;&Df6nd(W9ZC;W}%QzF-lfeX=QDvy?I{=RNX4?Wxu~n=v`g^ zRx;E)Pl-V$2XtsJjFH8Vn2o*@+d?^Y8B2sju2_m`rsk%_GJIO?kX4~Ntbf5etE}^# z*8fT*rQ9)NrYE5=(ri@7NQ+FGfrSZ@iIK7>kUftyszrf1C)5KUeBx8z@?MR>zGDL3 z+wuy7JwrYgGN$#fGE#$2Lbo+MfSTp*P=ZHlA7&x(HuI4I6 xgNa-h3UfPf(jw7f%98>EH!YUf%{a+Ip5%e6aD)nFgiSr4U>?+S5z8jN=pVlIo+$tT literal 0 HcmV?d00001 diff --git a/crates/guest-rust/rt/src/libwit_bindgen_cabi_realloc.a b/crates/guest-rust/rt/src/libwit_bindgen_cabi_realloc.a deleted file mode 100644 index c14633576beda7faa3bcd443d3b828bcc7df32f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 412 zcmZ{gzfQw25XSGEN-d#MA~q(B#8%p-QPc^s6lQjoixW(+a2(_~{F%y2@CdvoP76{% z;*)IoK7IGQlTR;%Egvq!CI78tQ10KuoAWgN3FILFpg*^zC=^*Kv~Eg(VYO(M{6FG4 z%XX-Az4}IaqG;|QJIQO zK6;12C;n-S0pid~{}qeP&DHe?VTy1bIbkd5r%T~FD_b^;>Tb8eywXh}bUT~0sxou= G>cTJbpJx&P diff --git a/crates/guest-rust/rt/src/cabi_realloc.c b/crates/guest-rust/rt/src/wit_bindgen_cabi.c similarity index 58% rename from crates/guest-rust/rt/src/cabi_realloc.c rename to crates/guest-rust/rt/src/wit_bindgen_cabi.c index 9847aef29..dc274e503 100644 --- a/crates/guest-rust/rt/src/cabi_realloc.c +++ b/crates/guest-rust/rt/src/wit_bindgen_cabi.c @@ -1,4 +1,4 @@ -// This file is generated by ./ci/rebuild-libcabi-realloc.sh +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh #include @@ -8,3 +8,10 @@ __attribute__((__weak__, __export_name__("cabi_realloc"))) void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { return cabi_realloc_wit_bindgen_0_41_0(ptr, old_size, align, new_size); } + +extern void *wasip3_task_set_wit_bindgen_0_41_0(void *ptr); + +__attribute__((__weak__)) +void *wasip3_task_set(void *ptr) { + return wasip3_task_set_wit_bindgen_0_41_0(ptr); +} diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi.o b/crates/guest-rust/rt/src/wit_bindgen_cabi.o new file mode 100644 index 0000000000000000000000000000000000000000..67fea0e8a1464f8e57f32b22da66b319f5307da6 GIT binary patch literal 378 zcmZutu};H447D$bT7`;4Vn7Uth&3uIf|&&vnD_+u65Qz2%O$$Iv~((rZ2SZ>zsR*L zfbd{hPtVWq8Ki3w0Ps-E<83J1&M`FchH=DZl@V!WSXyei?d(PX?BKInR*Ec3t-G4m z%2TDxQ^SU4bbU#)Z`S!*I@Mp1mu^Xp{eMK<$(;~|b!EBsv=~h7RcFFhr5XDm6$!vm zj+389L8PZ5hXM*LL=ojLq2M45K>oTP;bs(^FO_M;_;ZIO@PXpDMh=if*&mmXWAYw; za+F#Q=`L;`@9tv(6NKXk9x^X&6QV3w`oXe`%d{P|S1YLpF0*#n+nzSMtE6^iW_iJu SP0drk>DiTIry4WmXa5Byu5Yve literal 0 HcmV?d00001 diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi.rs b/crates/guest-rust/rt/src/wit_bindgen_cabi.rs new file mode 100644 index 000000000..3ebdc905e --- /dev/null +++ b/crates/guest-rust/rt/src/wit_bindgen_cabi.rs @@ -0,0 +1,22 @@ +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_41_0( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + crate::cabi_realloc(old_ptr, old_len, align, new_len) +} + +static mut WASIP3_TASK: *mut u8 = core::ptr::null_mut(); + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn wasip3_task_set_wit_bindgen_0_41_0(ptr: *mut u8) -> *mut u8 { + unsafe { + let ret = WASIP3_TASK; + WASIP3_TASK = ptr; + ret + } +} From 181912bab259f2077d1a76567d91f5c91cb82f33 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 2 Apr 2025 12:13:36 -0700 Subject: [PATCH 2/6] Refactor with separate objects Try to work around symbol/export trickery --- .github/workflows/main.yml | 4 +- ci/rebuild-libwit-bindgen-cabi.sh | 56 +++++++++--------- .../guest-rust/rt/src/libwit_bindgen_cabi.a | Bin 628 -> 912 bytes crates/guest-rust/rt/src/wit_bindgen_cabi.c | 7 ++- crates/guest-rust/rt/src/wit_bindgen_cabi.o | Bin 378 -> 414 bytes crates/guest-rust/rt/src/wit_bindgen_cabi.rs | 11 ---- .../rt/src/wit_bindgen_cabi_realloc.c | 10 ++++ .../rt/src/wit_bindgen_cabi_realloc.o | Bin 0 -> 294 bytes .../rt/src/wit_bindgen_cabi_realloc.rs | 11 ++++ .../rt/src/wit_bindgen_cabi_wasip3.c | 12 ++++ .../rt/src/wit_bindgen_cabi_wasip3.o | Bin 0 -> 271 bytes 11 files changed, 69 insertions(+), 42 deletions(-) create mode 100644 crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.c create mode 100644 crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.o create mode 100644 crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.rs create mode 100644 crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.c create mode 100644 crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.o diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 67a86dd80..185301efe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -151,10 +151,10 @@ jobs: - name: Install wasm32-wasip1 target run: rustup target add wasm32-wasip1 - # Verify the output of the `./ci/rebuild-libcabi-realloc.sh` script is + # Verify the output of the `./ci/rebuild-libwit-bindgen-cabi.sh` script is # up-to-date. - uses: ./.github/actions/install-wasi-sdk - - run: ./ci/rebuild-libcabi-realloc.sh + - run: ./ci/rebuild-libwit-bindgen-cabi.sh - run: git diff --exit-code # Test various feature combinations, make sure they all build diff --git a/ci/rebuild-libwit-bindgen-cabi.sh b/ci/rebuild-libwit-bindgen-cabi.sh index 1a903c06b..0caa6b89b 100755 --- a/ci/rebuild-libwit-bindgen-cabi.sh +++ b/ci/rebuild-libwit-bindgen-cabi.sh @@ -42,10 +42,11 @@ set -ex version=$(./ci/print-current-version.sh | sed 's/\./_/g') realloc=cabi_realloc_wit_bindgen_$version -wasip3_task_set=wasip3_task_set_wit_bindgen_$version -wasip3_task_get=wasip3_task_get_wit_bindgen_$version -cat >./crates/guest-rust/rt/src/wit_bindgen_cabi.rs <<-EOF +rm -f crates/guest-rust/rt/src/wit_bindgen_*.{rs,o,c} +rm -f crates/guest-rust/rt/src/libwit_bindgen_cabi.a + +cat >./crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.rs <<-EOF // This file is generated by $0 #[unsafe(no_mangle)] @@ -57,20 +58,9 @@ pub unsafe extern "C" fn $realloc( ) -> *mut u8 { crate::cabi_realloc(old_ptr, old_len, align, new_len) } - -static mut WASIP3_TASK: *mut u8 = core::ptr::null_mut(); - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn $wasip3_task_set(ptr: *mut u8) -> *mut u8 { - unsafe { - let ret = WASIP3_TASK; - WASIP3_TASK = ptr; - ret - } -} EOF -cat >./crates/guest-rust/rt/src/wit_bindgen_cabi.c <<-EOF +cat >./crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.c <<-EOF // This file is generated by $0 #include @@ -81,25 +71,37 @@ __attribute__((__weak__, __export_name__("cabi_realloc"))) void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { return $realloc(ptr, old_size, align, new_size); } +EOF + +cat >./crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.c <<-EOF +// This file is generated by $0 -extern void *$wasip3_task_set(void *ptr); +#include + +static void *WASIP3_TASK = NULL; __attribute__((__weak__)) void *wasip3_task_set(void *ptr) { - return $wasip3_task_set(ptr); + void *ret = WASIP3_TASK; + WASIP3_TASK = ptr; + return ret; } EOF -rm -f crates/guest-rust/rt/src/wit_bindgen_cabi.o -$WASI_SDK_PATH/bin/clang crates/guest-rust/rt/src/wit_bindgen_cabi.c \ - -O -c -o crates/guest-rust/rt/src/wit_bindgen_cabi.o +build() { + file=$1 + $WASI_SDK_PATH/bin/clang crates/guest-rust/rt/src/$1.c \ + -O -c -o crates/guest-rust/rt/src/$1.o + # Remove the `producers` section. This appears to differ whether the host for + # clang is either macOS or Linux. Not needed here anyway, so discard it to help + # either host produce the same object. + wasm-tools strip -d producers ./crates/guest-rust/rt/src/$1.o \ + -o ./crates/guest-rust/rt/src/$1.o +} -# Remove the `producers` section. This appears to differ whether the host for -# clang is either macOS or Linux. Not needed here anyway, so discard it to help -# either host produce the same object. -wasm-tools strip -d producers ./crates/guest-rust/rt/src/wit_bindgen_cabi.o \ - -o ./crates/guest-rust/rt/src/wit_bindgen_cabi.o +build wit_bindgen_cabi_realloc +build wit_bindgen_cabi_wasip3 -rm -f crates/guest-rust/rt/src/libwit_bindgen_cabi.a $WASI_SDK_PATH/bin/llvm-ar crus crates/guest-rust/rt/src/libwit_bindgen_cabi.a \ - crates/guest-rust/rt/src/wit_bindgen_cabi.o + crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.o \ + crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.o diff --git a/crates/guest-rust/rt/src/libwit_bindgen_cabi.a b/crates/guest-rust/rt/src/libwit_bindgen_cabi.a index afac3d661f9bd64b0d549812be0aeadf30b22c80..bd13c66e726144821f0cb89722a88544905cba34 100644 GIT binary patch delta 436 zcmZXQK~BOz7=`~g(~7htG$s;bj8-?|LI|L!%SK!n62xHQa$0ag8lae#NL-a8(BQ_M z8;;?^t9S+vU<)P^eY^SJ%)IaYU&=H!t*f6ztQIvJ?6exaZ669>I{p5zQy3Ptu{#oN z*L&znPjsBNE5=UXK2{PddM_Q6FHX#*Y>k{?03t#fwi3od$ZS5#!J195CmASZAhY=7 zCxD=Bz#x-Mn9StHBo^tEPxW1|=XzbX{+diU&GF}VbfoS2juUzD1dlars!z{DlXB*37+puniWq`)lD00a!&TtESV zMlh4Xjy)$cFFP|Yorz;TNQ9MPDI*iA83GIptjzpq?qCpLWSszXxiD8zD$pW5XKsHN RR~9A)9!3Un=E*-9y#e*ULdF09 diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi.c b/crates/guest-rust/rt/src/wit_bindgen_cabi.c index dc274e503..e7cf56f5f 100644 --- a/crates/guest-rust/rt/src/wit_bindgen_cabi.c +++ b/crates/guest-rust/rt/src/wit_bindgen_cabi.c @@ -1,6 +1,7 @@ // This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh #include +#include extern void *cabi_realloc_wit_bindgen_0_41_0(void *ptr, size_t old_size, size_t align, size_t new_size); @@ -9,9 +10,11 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { return cabi_realloc_wit_bindgen_0_41_0(ptr, old_size, align, new_size); } -extern void *wasip3_task_set_wit_bindgen_0_41_0(void *ptr); +static void *WASIP3_TASK = NULL; __attribute__((__weak__)) void *wasip3_task_set(void *ptr) { - return wasip3_task_set_wit_bindgen_0_41_0(ptr); + void *ret = WASIP3_TASK; + WASIP3_TASK = ptr; + return ret; } diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi.o b/crates/guest-rust/rt/src/wit_bindgen_cabi.o index 67fea0e8a1464f8e57f32b22da66b319f5307da6..905870b49dd98b81a4804f0324229e28f57e11b9 100644 GIT binary patch delta 239 zcmX|(F-yZh7>3{PyB?NYpc$<=T&IddIz-$L>mVHzDs;SF%PFC0D!HOK)ycV~&Tjq; z|Cp1B1)uHV<$ZqmQ*684sSv(=?pRyorFWHG`sJ#6Wza4=-@RXKC?<*qoIpd9W-XGp z8mLpEjlmiJS&vM;Umir0QW3#Gwb1ZzRHqkt`N-|><|^?;6wlo(x0QEAv6>?RBzIn1 z$e%L1c41+IUlXf|8IEu6GJ9{vBcLU>1o_c4C`o4_q!aT$GjJSLzOJQLx5H~G5R*6& K5PgCDfJVPJgE#sB delta 209 zcmbQo{EKOVq*Ny(3v+5-8Gn3yPG(+eVo`i_@vCdl=RfRc!PKo!*~M*#)%OY^-Nr{OacrF3<``2ObW~b4M4!a%>@(? zXaqAE?AU?kXJ_W6GjXg3iLf#(Wn|(>PE5*-FG@|!$;nS<5MW?nW#&h-kwJiwbpj&; lgD_W7Do~N0v%iZg3ljqmBZD|IgC~DUVo^HKS(9TK9RcynIY0ma diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi.rs b/crates/guest-rust/rt/src/wit_bindgen_cabi.rs index 3ebdc905e..126cd2142 100644 --- a/crates/guest-rust/rt/src/wit_bindgen_cabi.rs +++ b/crates/guest-rust/rt/src/wit_bindgen_cabi.rs @@ -9,14 +9,3 @@ pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_41_0( ) -> *mut u8 { crate::cabi_realloc(old_ptr, old_len, align, new_len) } - -static mut WASIP3_TASK: *mut u8 = core::ptr::null_mut(); - -#[unsafe(no_mangle)] -pub unsafe extern "C" fn wasip3_task_set_wit_bindgen_0_41_0(ptr: *mut u8) -> *mut u8 { - unsafe { - let ret = WASIP3_TASK; - WASIP3_TASK = ptr; - ret - } -} diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.c b/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.c new file mode 100644 index 000000000..4fa88e67e --- /dev/null +++ b/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.c @@ -0,0 +1,10 @@ +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh + +#include + +extern void *cabi_realloc_wit_bindgen_0_41_0(void *ptr, size_t old_size, size_t align, size_t new_size); + +__attribute__((__weak__, __export_name__("cabi_realloc"))) +void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { + return cabi_realloc_wit_bindgen_0_41_0(ptr, old_size, align, new_size); +} diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.o b/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.o new file mode 100644 index 0000000000000000000000000000000000000000..c02415fd37c468a323c0cac897e0efcf335fca0d GIT binary patch literal 294 zcmZ9HK~BRk5JhK_pthk>EOxBO5-AEQ5;p)AtT@8dF;mN`?Nqi?=&BrnEeGJ9xUfO| z<Fv zF6N&nQovPlp}HZXS*HJ-?-I$mCY` literal 0 HcmV?d00001 diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.rs b/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.rs new file mode 100644 index 000000000..126cd2142 --- /dev/null +++ b/crates/guest-rust/rt/src/wit_bindgen_cabi_realloc.rs @@ -0,0 +1,11 @@ +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_41_0( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + crate::cabi_realloc(old_ptr, old_len, align, new_len) +} diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.c b/crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.c new file mode 100644 index 000000000..2e4a50617 --- /dev/null +++ b/crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.c @@ -0,0 +1,12 @@ +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh + +#include + +static void *WASIP3_TASK = NULL; + +__attribute__((__weak__)) +void *wasip3_task_set(void *ptr) { + void *ret = WASIP3_TASK; + WASIP3_TASK = ptr; + return ret; +} diff --git a/crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.o b/crates/guest-rust/rt/src/wit_bindgen_cabi_wasip3.o new file mode 100644 index 0000000000000000000000000000000000000000..41f77bd73ac9fe9cd35c333f4c4ae854f8f78df8 GIT binary patch literal 271 zcmYk0!AiqG7=-8Fq_!qPyjVPl)SD2JOTl~49*Uqs1&`~d{0*VIN!i_0dx{U?EBKzK zcyVsSe0&U)GX#K6=$(4%#U%yY>=P+<-LSIKuxafDZ z_g7{V#}T1G91c$4X*^iAbyJDS&k+_FA^A|Q-p!>~ZYv#o67gHHTE5KXYq5F+!qN8; zrSrUWF8?|j{{@9kMV|lw literal 0 HcmV?d00001 From 29295f76e85272d82d5a1379a8843194a2a8acb5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 2 Apr 2025 14:14:20 -0500 Subject: [PATCH 3/6] Update crates/guest-rust/rt/src/async_support/waitable.rs Co-authored-by: Joel Dice --- crates/guest-rust/rt/src/async_support/waitable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 2f7dd235d..9b4c4103b 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -203,7 +203,7 @@ where // SAFETY: the contract of `wasip3_task_set` is that the returned // pointer is valid for the lifetime of our entire task, so it's valid // for this stack frame. Additionally we assert it's non-null to - // double-check it's initialized and additionlly check the version for + // double-check it's initialized and additionally check the version for // the fields that we access. // // Otherwise the `waitable_unregister` callback should be safe if we From 04f42534f4991eb387f6f5cd8cc8ebba24f497a2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 2 Apr 2025 12:17:14 -0700 Subject: [PATCH 4/6] Update wasi-sdk in CI --- .github/actions/install-wasi-sdk/action.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/install-wasi-sdk/action.yml b/.github/actions/install-wasi-sdk/action.yml index 468e3873a..6132d9965 100644 --- a/.github/actions/install-wasi-sdk/action.yml +++ b/.github/actions/install-wasi-sdk/action.yml @@ -5,18 +5,18 @@ runs: using: composite steps: - run: | - curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-24/wasi-sdk-24.0-x86_64-linux.tar.gz -L | tar xzvf - - echo "WASI_SDK_PATH=`pwd`/wasi-sdk-24.0-x86_64-linux" >> $GITHUB_ENV + curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz -L | tar xzvf - + echo "WASI_SDK_PATH=`pwd`/wasi-sdk-25.0-x86_64-linux" >> $GITHUB_ENV if: runner.os == 'Linux' shell: bash - run: | - curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-24/wasi-sdk-24.0-x86_64-macos.tar.gz -L | tar xzvf - - echo "WASI_SDK_PATH=`pwd`/wasi-sdk-24.0-x86_64-macos" >> $GITHUB_ENV + curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-macos.tar.gz -L | tar xzvf - + echo "WASI_SDK_PATH=`pwd`/wasi-sdk-25.0-x86_64-macos" >> $GITHUB_ENV if: runner.os == 'macOS' shell: bash - run: | - curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-24/wasi-sdk-24.0-x86_64-windows.tar.gz -L | tar xzvf - - echo "WASI_SDK_PATH=`pwd`/wasi-sdk-24.0-x86_64-windows" >> $GITHUB_ENV + curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-windows.tar.gz -L | tar xzvf - + echo "WASI_SDK_PATH=`pwd`/wasi-sdk-25.0-x86_64-windows" >> $GITHUB_ENV if: runner.os == 'Windows' shell: bash - name: Setup `wasm-tools` From c0216a145f9c92a52cd176d0764d867871d0196e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 3 Apr 2025 08:29:33 -0700 Subject: [PATCH 5/6] Review comments --- crates/guest-rust/rt/src/async_support/cabi.rs | 10 +++++----- crates/guest-rust/rt/src/async_support/waitable.rs | 9 +++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/guest-rust/rt/src/async_support/cabi.rs b/crates/guest-rust/rt/src/async_support/cabi.rs index 59958da8c..e3afd7a08 100644 --- a/crates/guest-rust/rt/src/async_support/cabi.rs +++ b/crates/guest-rust/rt/src/async_support/cabi.rs @@ -12,12 +12,12 @@ //! `std::future::Future`. //! //! Without this module this situation won't work because 0.A.0 has no -//! knowledge of 0.B.0 meaning that when 0.B.0 decides to block it won't know -//! where to register its `waitable` within a `waitable-set`. +//! knowledge of 0.B.0 meaning that 0.B.0 has no means of inserting a `waitable` +//! into the `waitable-set` managed by 0.A.0's export. //! //! To solve this problem the long-term intention is that something will live //! in `wasi-libc` itself, but in the meantime it's living "somewhere" within -//! `wit-bindgen 0.*.0`. Specifically all `wit-bindgen` versions will all +//! `wit-bindgen 0.*.0`. Specifically all `wit-bindgen` versions will //! reference, via C linkage, a single function which is used to manipulate a //! single pointer in linear memory. This pointer is a `wasip3_task` structure //! which has all the various fields to use it. @@ -50,7 +50,7 @@ extern "C" { /// Sets the global task pointer to `ptr` provided. Returns the previous /// value. /// - /// This function acts as both a dual getter and a setter. To get the + /// This function acts as a dual getter and a setter. To get the /// current task pointer a dummy `ptr` can be provided (e.g. NULL) and then /// it's passed back when you're done working with it. When setting the /// current task pointer it's recommended to call this and then call it @@ -85,7 +85,7 @@ pub struct wasip3_task { /// well as the `callback_ptr` provided. /// /// If `waitable` was previously registered with this task then the - /// previuos `callback_ptr` is returned. Otherwise `NULL` is returned. + /// previous `callback_ptr` is returned. Otherwise `NULL` is returned. /// /// It's the caller's responsibility to ensure that `callback_ptr` is valid /// until `callback` is invoked, `waitable_unregister` is invoked, or diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 9b4c4103b..129151394 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -206,8 +206,13 @@ where // double-check it's initialized and additionally check the version for // the fields that we access. // - // Otherwise the `waitable_unregister` callback should be safe if we - // pass a waitable that we own and the task's own pointer. + // Otherwise the `waitable_unregister` callback should be safe because: + // + // * We're fulfilling the contract where the first argument must be + // `(*task).ptr` + // * We own the `waitable` that we're passing in, so we're fulfilling + // the contract that arbitrary waitables for other units of work + // aren't being manipulated. unsafe { let task = cabi::wasip3_task_set(ptr::null_mut()); assert!(!task.is_null()); From bd50ee294b8dbfb0324aa7d5aa858f4c58f12559 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 3 Apr 2025 09:06:38 -0700 Subject: [PATCH 6/6] Get crate compiling on native --- crates/guest-rust/rt/src/async_support/cabi.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/guest-rust/rt/src/async_support/cabi.rs b/crates/guest-rust/rt/src/async_support/cabi.rs index e3afd7a08..be9945de9 100644 --- a/crates/guest-rust/rt/src/async_support/cabi.rs +++ b/crates/guest-rust/rt/src/async_support/cabi.rs @@ -46,6 +46,7 @@ use core::ffi::c_void; +#[cfg(target_family = "wasm")] extern "C" { /// Sets the global task pointer to `ptr` provided. Returns the previous /// value. @@ -61,6 +62,12 @@ extern "C" { pub fn wasip3_task_set(ptr: *mut wasip3_task) -> *mut wasip3_task; } +#[cfg(not(target_family = "wasm"))] +pub unsafe extern "C" fn wasip3_task_set(ptr: *mut wasip3_task) -> *mut wasip3_task { + let _ = ptr; + unreachable!(); +} + /// The first version of `wasip3_task` which implies the existence of the /// fields `ptr`, `waitable_register`, and `waitable_unregister`. pub const WASIP3_TASK_V1: u32 = 1;