Skip to content

Commit

Permalink
rust: Store raw intrinsics in vtables (#1187)
Browse files Browse the repository at this point in the history
Instead of generating a wrapper-per-intrinsic try to instead store raw
functions inside of vtables to push as much code as possible into `mod
async_support`, as it's generally easier to grok code in one location
rather than generated code.

Note that this does not update the `read` or `write` vtable functions as
they're going to be trickier, if even possible, to expose the more raw
version.

This shouldn't have any functional change, it's just a reorganizing.
The `cancel_*` intrinsics are all marked with a `TODO`, however, to come
back and reconsider the return value which is otherwise discarded right
now.
  • Loading branch information
alexcrichton authored Mar 4, 2025
1 parent ccebf1d commit a2d6b03
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 268 deletions.
4 changes: 2 additions & 2 deletions crates/guest-rust/rt/src/async_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ mod future_support;
mod stream_support;

pub use {
future_support::{FutureReader, FutureVtable, FutureWriter},
stream_support::{StreamReader, StreamVtable, StreamWriter},
future_support::{future_new, FutureReader, FutureVtable, FutureWriter},
stream_support::{stream_new, StreamReader, StreamVtable, StreamWriter},
};

pub use futures;
Expand Down
55 changes: 41 additions & 14 deletions crates/guest-rust/rt/src/async_support/future_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,29 @@ use {
pub struct FutureVtable<T> {
pub write: fn(future: u32, value: T) -> Pin<Box<dyn Future<Output = bool>>>,
pub read: fn(future: u32) -> Pin<Box<dyn Future<Output = Option<Result<T, ErrorContext>>>>>,
pub cancel_write: fn(future: u32),
pub cancel_read: fn(future: u32),
pub close_writable: fn(future: u32, err_ctx: u32),
pub close_readable: fn(future: u32),
pub cancel_write: unsafe extern "C" fn(future: u32) -> u32,
pub cancel_read: unsafe extern "C" fn(future: u32) -> u32,
pub close_writable: unsafe extern "C" fn(future: u32, err_ctx: u32),
pub close_readable: unsafe extern "C" fn(future: u32),
pub new: unsafe extern "C" fn() -> u32,
}

/// Helper function to create a new read/write pair for a component model
/// future.
pub unsafe fn future_new<T>(
vtable: &'static FutureVtable<T>,
) -> (FutureWriter<T>, FutureReader<T>) {
let handle = unsafe { (vtable.new)() };
super::with_entry(handle, |entry| match entry {
Entry::Vacant(entry) => {
entry.insert(Handle::LocalOpen);
}
Entry::Occupied(_) => unreachable!(),
});
(
FutureWriter::new(handle, vtable),
FutureReader::new(handle, vtable),
)
}

/// Represents the writable end of a Component Model `future`.
Expand Down Expand Up @@ -84,7 +103,11 @@ impl<T> CancelableWrite<T> {
Handle::LocalReady(..) => {
entry.insert(Handle::LocalOpen);
}
Handle::Write => (writer.vtable.cancel_write)(writer.handle),
Handle::Write => unsafe {
// TODO: spec-wise this can return `BLOCKED` which seems
// bad?
(writer.vtable.cancel_write)(writer.handle);
},
},
});
writer
Expand Down Expand Up @@ -179,17 +202,17 @@ impl<T> Drop for FutureWriter<T> {
entry.insert(Handle::LocalClosed);
}
Handle::Read => unreachable!(),
Handle::Write | Handle::LocalClosed => {
Handle::Write | Handle::LocalClosed => unsafe {
entry.remove();
(self.vtable.close_writable)(self.handle, 0);
}
},
Handle::WriteClosedErr(_) => match entry.remove() {
Handle::WriteClosedErr(None) => {
Handle::WriteClosedErr(None) => unsafe {
(self.vtable.close_writable)(self.handle, 0);
}
Handle::WriteClosedErr(Some(err_ctx)) => {
},
Handle::WriteClosedErr(Some(err_ctx)) => unsafe {
(self.vtable.close_writable)(self.handle, err_ctx.handle());
}
},
_ => unreachable!(),
},
},
Expand Down Expand Up @@ -240,7 +263,11 @@ impl<T> CancelableRead<T> {
Handle::LocalWaiting(_) => {
entry.insert(Handle::LocalOpen);
}
Handle::Read => (reader.vtable.cancel_read)(handle),
Handle::Read => unsafe {
// TODO: spec-wise this can return `BLOCKED` which seems
// bad?
(reader.vtable.cancel_read)(handle);
},
},
});
reader
Expand Down Expand Up @@ -389,10 +416,10 @@ impl<T> Drop for FutureReader<T> {
Handle::LocalOpen | Handle::LocalWaiting(_) => {
entry.insert(Handle::LocalClosed);
}
Handle::Read | Handle::LocalClosed => {
Handle::Read | Handle::LocalClosed => unsafe {
entry.remove();
(self.vtable.close_readable)(handle);
}
},
Handle::Write | Handle::WriteClosedErr(_) => unreachable!(),
},
});
Expand Down
54 changes: 41 additions & 13 deletions crates/guest-rust/rt/src/async_support/stream_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,30 @@ pub struct StreamVtable<T> {
future: u32,
values: &mut [MaybeUninit<T>],
) -> Pin<Box<dyn Future<Output = Option<Result<usize, ErrorContext>>> + '_>>,
pub cancel_write: fn(future: u32),
pub cancel_read: fn(future: u32),
pub close_writable: fn(future: u32, err_ctx: u32),
pub close_readable: fn(future: u32),
pub cancel_write: unsafe extern "C" fn(future: u32) -> u32,
pub cancel_read: unsafe extern "C" fn(future: u32) -> u32,
pub close_writable: unsafe extern "C" fn(future: u32, err_ctx: u32),
pub close_readable: unsafe extern "C" fn(future: u32),
pub new: unsafe extern "C" fn() -> u32,
}

/// Helper function to create a new read/write pair for a component model
/// stream.
pub unsafe fn stream_new<T>(
vtable: &'static StreamVtable<T>,
) -> (StreamWriter<T>, StreamReader<T>) {
let handle = unsafe { (vtable.new)() };
super::with_entry(handle, |entry| match entry {
Entry::Vacant(entry) => {
entry.insert(Handle::LocalOpen);
}
Entry::Occupied(_) => unreachable!(),
});
(
StreamWriter::new(handle, vtable),
StreamReader::new(handle, vtable),
)
}
struct CancelWriteOnDrop<T: 'static> {
handle: Option<u32>,
vtable: &'static StreamVtable<T>,
Expand All @@ -60,7 +78,11 @@ impl<T> Drop for CancelWriteOnDrop<T> {
Handle::LocalReady(..) => {
entry.insert(Handle::LocalOpen);
}
Handle::Write => (self.vtable.cancel_write)(handle),
Handle::Write => unsafe {
// TODO: spec-wise this can return `BLOCKED` which seems
// bad?
(self.vtable.cancel_write)(handle);
},
},
});
}
Expand Down Expand Up @@ -221,18 +243,20 @@ impl<T> Drop for StreamWriter<T> {
entry.insert(Handle::LocalClosed);
}
Handle::Read => unreachable!(),
Handle::Write | Handle::LocalClosed => {
Handle::Write | Handle::LocalClosed => unsafe {
entry.remove();
(self.vtable.close_writable)(self.handle, 0);
}
},
Handle::WriteClosedErr(_) => match entry.remove() {
// Care is taken to avoid dropping the ErrorContext before close_writable is called.
// If the error context is dropped prematurely, the component may garbage collect
// the error context before it can be used/referenced by close_writable().
Handle::WriteClosedErr(Some(e)) => {
Handle::WriteClosedErr(Some(e)) => unsafe {
(self.vtable.close_writable)(self.handle, e.handle)
}
Handle::WriteClosedErr(None) => (self.vtable.close_writable)(self.handle, 0),
},
Handle::WriteClosedErr(None) => unsafe {
(self.vtable.close_writable)(self.handle, 0)
},
_ => unreachable!(),
},
},
Expand All @@ -259,7 +283,11 @@ impl<T> Drop for CancelReadOnDrop<T> {
Handle::LocalWaiting(_) => {
entry.insert(Handle::LocalOpen);
}
Handle::Read => (self.vtable.cancel_read)(handle),
Handle::Read => unsafe {
// TODO: spec-wise this can return `BLOCKED` which seems
// bad?
(self.vtable.cancel_read)(handle);
},
},
});
}
Expand Down Expand Up @@ -457,10 +485,10 @@ impl<T> Drop for StreamReader<T> {
Handle::LocalOpen | Handle::LocalWaiting(_) => {
entry.insert(Handle::LocalClosed);
}
Handle::Read | Handle::LocalClosed => {
Handle::Read | Handle::LocalClosed => unsafe {
entry.remove();
(self.vtable.close_readable)(handle);
}
},
Handle::Write | Handle::WriteClosedErr(_) => unreachable!(),
},
});
Expand Down
Loading

0 comments on commit a2d6b03

Please sign in to comment.