Skip to content

Commit 416c5da

Browse files
committed
flesh out async support in wit-dylib
This does two things: First, it adds a low-level API to `wit-dylib-ffi` for async imports and exports as an alternative to the high-level API based on `wit-bindgen`'s `rt` library and enabled with the `async` feature. Thus enabling the `async` feature now really just means "use the high-level API", while disabling it means "use the low-level API". The low-level API can make more sense for e.g. Python, where the coroutine model is different enough from Rust's that it's easier to map it directly to the component model ABI rather than try to plumb it through Rust's async APIs. Second, it populates the `wit_future` and `wit_stream` types with all the relevant intrinsics, ABI size/align metadata, and generated payload lift and lower functions. I've tested this all end-to-end with `componentize-py`, but could use some guidance on how to add tests here as well. Signed-off-by: Joel Dice <[email protected]>
1 parent 43f58ad commit 416c5da

File tree

8 files changed

+655
-25
lines changed

8 files changed

+655
-25
lines changed

crates/wit-dylib/ffi/src/ffi.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub const WIT_TYPE_FUTURE: u32 = 25;
2929
pub const WIT_TYPE_STREAM: u32 = 26;
3030
pub const WIT_TYPE_ALIAS: u32 = 27;
3131
pub const WIT_TYPE_EMPTY: u32 = 255;
32-
pub const WIT_CURRENT_VERSION: u32 = 1;
32+
pub const WIT_CURRENT_VERSION: u32 = 2;
3333
pub type wit_type_t = u32;
3434
pub type wit_import_fn_t =
3535
::std::option::Option<unsafe extern "C" fn(cx: *mut ::std::os::raw::c_void)>;
@@ -176,20 +176,74 @@ pub struct wit_fixed_size_list {
176176
pub ty: wit_type_t,
177177
}
178178
pub type wit_fixed_size_list_t = wit_fixed_size_list;
179+
pub type wit_lift_fn_t = ::std::option::Option<
180+
unsafe extern "C" fn(cx: *mut ::std::os::raw::c_void, buffer: *const ::std::os::raw::c_void),
181+
>;
182+
pub type wit_lower_fn_t = ::std::option::Option<
183+
unsafe extern "C" fn(cx: *mut ::std::os::raw::c_void, buffer: *mut ::std::os::raw::c_void),
184+
>;
185+
pub type wit_future_new_fn_t = ::std::option::Option<unsafe extern "C" fn() -> u64>;
186+
pub type wit_future_read_fn_t = ::std::option::Option<
187+
unsafe extern "C" fn(future: u32, buffer: *mut ::std::os::raw::c_void) -> u32,
188+
>;
189+
pub type wit_future_write_fn_t = ::std::option::Option<
190+
unsafe extern "C" fn(future: u32, buffer: *const ::std::os::raw::c_void) -> u32,
191+
>;
192+
pub type wit_future_cancel_read_fn_t =
193+
::std::option::Option<unsafe extern "C" fn(future: u32) -> u32>;
194+
pub type wit_future_cancel_write_fn_t =
195+
::std::option::Option<unsafe extern "C" fn(future: u32) -> u32>;
196+
pub type wit_future_drop_readable_fn_t = ::std::option::Option<unsafe extern "C" fn(future: u32)>;
197+
pub type wit_future_drop_writable_fn_t = ::std::option::Option<unsafe extern "C" fn(future: u32)>;
179198
#[repr(C)]
180199
#[derive(Debug, Copy, Clone)]
181200
pub struct wit_future {
182201
pub interface: *const ::std::os::raw::c_char,
183202
pub name: *const ::std::os::raw::c_char,
184203
pub ty: wit_type_t,
204+
pub new: wit_future_new_fn_t,
205+
pub read: wit_future_read_fn_t,
206+
pub write: wit_future_write_fn_t,
207+
pub cancel_read: wit_future_cancel_read_fn_t,
208+
pub cancel_write: wit_future_cancel_write_fn_t,
209+
pub drop_readable: wit_future_drop_readable_fn_t,
210+
pub drop_writable: wit_future_drop_writable_fn_t,
211+
pub lift: wit_lift_fn_t,
212+
pub lower: wit_lower_fn_t,
213+
pub abi_payload_size: usize,
214+
pub abi_payload_align: usize,
185215
}
186216
pub type wit_future_t = wit_future;
217+
pub type wit_stream_new_fn_t = ::std::option::Option<unsafe extern "C" fn() -> u64>;
218+
pub type wit_stream_read_fn_t = ::std::option::Option<
219+
unsafe extern "C" fn(stream: u32, buffer: *mut ::std::os::raw::c_void, count: usize) -> u32,
220+
>;
221+
pub type wit_stream_write_fn_t = ::std::option::Option<
222+
unsafe extern "C" fn(stream: u32, buffer: *const ::std::os::raw::c_void, count: usize) -> u32,
223+
>;
224+
pub type wit_stream_cancel_read_fn_t =
225+
::std::option::Option<unsafe extern "C" fn(stream: u32) -> u32>;
226+
pub type wit_stream_cancel_write_fn_t =
227+
::std::option::Option<unsafe extern "C" fn(stream: u32) -> u32>;
228+
pub type wit_stream_drop_writable_fn_t = ::std::option::Option<unsafe extern "C" fn(stream: u32)>;
229+
pub type wit_stream_drop_readable_fn_t = ::std::option::Option<unsafe extern "C" fn(stream: u32)>;
187230
#[repr(C)]
188231
#[derive(Debug, Copy, Clone)]
189232
pub struct wit_stream {
190233
pub interface: *const ::std::os::raw::c_char,
191234
pub name: *const ::std::os::raw::c_char,
192235
pub ty: wit_type_t,
236+
pub new: wit_stream_new_fn_t,
237+
pub read: wit_stream_read_fn_t,
238+
pub write: wit_stream_write_fn_t,
239+
pub cancel_read: wit_stream_cancel_read_fn_t,
240+
pub cancel_write: wit_stream_cancel_write_fn_t,
241+
pub drop_readable: wit_stream_drop_readable_fn_t,
242+
pub drop_writable: wit_stream_drop_writable_fn_t,
243+
pub lift: wit_lift_fn_t,
244+
pub lower: wit_lower_fn_t,
245+
pub abi_payload_size: usize,
246+
pub abi_payload_align: usize,
193247
}
194248
pub type wit_stream_t = wit_stream;
195249
#[repr(C)]

crates/wit-dylib/ffi/src/lib.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -372,12 +372,19 @@ pub trait Interpreter: 'static {
372372

373373
fn export_call(wit: Wit, func: ExportFunction, cx: &mut Self::CallCx<'_>);
374374

375+
#[cfg(feature = "async")]
375376
fn export_call_async(
376377
wit: Wit,
377378
func: ExportFunction,
378379
cx: Box<Self::CallCx<'static>>,
379380
) -> impl std::future::Future<Output = ()>;
380381

382+
#[cfg(not(feature = "async"))]
383+
fn export_async_start(wit: Wit, func: ExportFunction, cx: Box<Self::CallCx<'static>>) -> u32;
384+
385+
#[cfg(not(feature = "async"))]
386+
fn export_async_callback(event0: u32, event1: u32, event2: u32) -> u32;
387+
381388
fn export_finish(cx: Box<Self::CallCx<'_>>, func: ExportFunction) {
382389
let _ = func;
383390
let _ = cx;
@@ -519,20 +526,22 @@ pub trait RawInterpreter: Interpreter {
519526

520527
unsafe fn raw_export_async_call(cx: *mut u8, which: usize) -> u32 {
521528
debug_println!("export_async_call({cx:?}, {which})");
522-
#[cfg(feature = "async")]
523529
unsafe {
524530
let wit = Wit::from_raw(WIT_T);
525531
let func = wit.export_func(which);
526-
wit_bindgen::rt::async_support::start_task(Self::export_call_async(
527-
wit,
528-
func,
529-
Box::from_raw(cx.cast()),
530-
))
531-
.cast_unsigned()
532-
}
533-
#[cfg(not(feature = "async"))]
534-
unsafe {
535-
panic!("async support disabled at compile time");
532+
#[cfg(feature = "async")]
533+
{
534+
wit_bindgen::rt::async_support::start_task(Self::export_call_async(
535+
wit,
536+
func,
537+
Box::from_raw(cx.cast()),
538+
))
539+
.cast_unsigned()
540+
}
541+
#[cfg(not(feature = "async"))]
542+
{
543+
Self::export_async_start(wit, func, Box::from_raw(cx.cast()))
544+
}
536545
}
537546
}
538547

@@ -543,7 +552,7 @@ pub trait RawInterpreter: Interpreter {
543552
wit_bindgen::rt::async_support::callback(a, b, c)
544553
}
545554
#[cfg(not(feature = "async"))]
546-
panic!("async support disabled at compile time");
555+
Self::export_async_callback(a, b, c)
547556
}
548557

549558
unsafe fn raw_export_finish(cx: *mut u8, which: usize) {

crates/wit-dylib/ffi/src/types.rs

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::Call;
22
use crate::ffi;
3-
use std::ffi::{CStr, c_char};
3+
use std::ffi::{CStr, c_char, c_void};
44
use std::fmt;
55
use std::hash::{Hash, Hasher};
66

@@ -329,6 +329,11 @@ unsafe fn slice<T>(ptr: *const T, len: usize) -> &'static [T] {
329329
}
330330
}
331331

332+
pub struct PendingAsyncImportCall {
333+
pub subtask: u32,
334+
pub buffer: *mut u8,
335+
}
336+
332337
#[derive(Copy, Clone)]
333338
pub struct ImportFunction {
334339
wit: Wit,
@@ -358,6 +363,10 @@ impl ImportFunction {
358363
self.ptr.async_impl
359364
}
360365

366+
pub fn async_import_lift_impl(&self) -> ffi::wit_import_async_lift_fn_t {
367+
self.ptr.async_lift_impl
368+
}
369+
361370
pub fn is_async(&self) -> bool {
362371
self.ptr.async_impl.is_some()
363372
}
@@ -384,6 +393,40 @@ impl ImportFunction {
384393
}
385394
}
386395

396+
#[cfg(not(feature = "async"))]
397+
pub unsafe fn call_import_async(&self, cx: &mut impl Call) -> Option<PendingAsyncImportCall> {
398+
use core::alloc::Layout;
399+
400+
const STATUS_STARTING: u32 = 0;
401+
const STATUS_STARTED: u32 = 1;
402+
const STATUS_RETURNED: u32 = 2;
403+
404+
let layout =
405+
Layout::from_size_align(self.ptr.async_abi_area_size, self.ptr.async_abi_area_align)
406+
.unwrap();
407+
let (status, buffer) = unsafe {
408+
let buffer = std::alloc::alloc(layout);
409+
cx.defer_deallocate(buffer, layout);
410+
let status = self.ptr.async_impl.unwrap()((&raw mut *cx).cast(), buffer.cast());
411+
(status, buffer)
412+
};
413+
let subtask = status >> 4;
414+
let status = status & 0xF;
415+
match status {
416+
STATUS_STARTING | STATUS_STARTED => Some(PendingAsyncImportCall { subtask, buffer }),
417+
STATUS_RETURNED => unsafe {
418+
self.lift_import_async_result(cx, buffer);
419+
None
420+
},
421+
_ => panic!("unexpected status from async import call: {status}"),
422+
}
423+
}
424+
425+
#[cfg(not(feature = "async"))]
426+
pub unsafe fn lift_import_async_result(&self, cx: &mut impl Call, buffer: *mut u8) {
427+
unsafe { self.ptr.async_lift_impl.unwrap()((&raw mut *cx).cast(), buffer.cast()) };
428+
}
429+
387430
#[cfg(feature = "async")]
388431
pub async fn call_import_async(&self, cx: &mut impl Call) {
389432
use core::alloc::Layout;
@@ -467,6 +510,14 @@ impl ExportFunction {
467510
self.ptr.task_return
468511
}
469512

513+
pub fn call_task_return(&self, cx: &mut impl Call) {
514+
let task_return = self.task_return().unwrap();
515+
unsafe {
516+
let cx: *mut _ = cx;
517+
task_return(cx.cast());
518+
}
519+
}
520+
470521
pub fn params(&self) -> impl ExactSizeIterator<Item = Type> + DoubleEndedIterator + Clone + '_ {
471522
self.raw_params()
472523
.iter()
@@ -1013,6 +1064,50 @@ impl Future {
10131064
pub fn ty(&self) -> Option<Type> {
10141065
Type::from_raw_opt(self.wit, self.ptr.ty)
10151066
}
1067+
1068+
pub fn new(&self) -> unsafe extern "C" fn() -> u64 {
1069+
self.ptr.new.unwrap()
1070+
}
1071+
1072+
pub fn read(&self) -> unsafe extern "C" fn(u32, *mut c_void) -> u32 {
1073+
self.ptr.read.unwrap()
1074+
}
1075+
1076+
pub fn write(&self) -> unsafe extern "C" fn(u32, *const c_void) -> u32 {
1077+
self.ptr.write.unwrap()
1078+
}
1079+
1080+
pub fn cancel_read(&self) -> unsafe extern "C" fn(u32) -> u32 {
1081+
self.ptr.cancel_read.unwrap()
1082+
}
1083+
1084+
pub fn cancel_write(&self) -> unsafe extern "C" fn(u32) -> u32 {
1085+
self.ptr.cancel_write.unwrap()
1086+
}
1087+
1088+
pub fn drop_readable(&self) -> unsafe extern "C" fn(u32) {
1089+
self.ptr.drop_readable.unwrap()
1090+
}
1091+
1092+
pub fn drop_writable(&self) -> unsafe extern "C" fn(u32) {
1093+
self.ptr.drop_writable.unwrap()
1094+
}
1095+
1096+
pub unsafe fn lift(&self, cx: &mut impl Call, buffer: *mut u8) {
1097+
unsafe { self.ptr.lift.unwrap()((&raw mut *cx).cast(), buffer.cast()) };
1098+
}
1099+
1100+
pub unsafe fn lower(&self, cx: &mut impl Call, buffer: *mut u8) {
1101+
unsafe { self.ptr.lower.unwrap()((&raw mut *cx).cast(), buffer.cast()) };
1102+
}
1103+
1104+
pub fn abi_payload_size(&self) -> usize {
1105+
self.ptr.abi_payload_size
1106+
}
1107+
1108+
pub fn abi_payload_align(&self) -> usize {
1109+
self.ptr.abi_payload_align
1110+
}
10161111
}
10171112

10181113
impl fmt::Debug for Future {
@@ -1049,6 +1144,50 @@ impl Stream {
10491144
pub fn ty(&self) -> Option<Type> {
10501145
Type::from_raw_opt(self.wit, self.ptr.ty)
10511146
}
1147+
1148+
pub fn new(&self) -> unsafe extern "C" fn() -> u64 {
1149+
self.ptr.new.unwrap()
1150+
}
1151+
1152+
pub fn read(&self) -> unsafe extern "C" fn(u32, *mut c_void, usize) -> u32 {
1153+
self.ptr.read.unwrap()
1154+
}
1155+
1156+
pub fn write(&self) -> unsafe extern "C" fn(u32, *const c_void, usize) -> u32 {
1157+
self.ptr.write.unwrap()
1158+
}
1159+
1160+
pub fn cancel_read(&self) -> unsafe extern "C" fn(u32) -> u32 {
1161+
self.ptr.cancel_read.unwrap()
1162+
}
1163+
1164+
pub fn cancel_write(&self) -> unsafe extern "C" fn(u32) -> u32 {
1165+
self.ptr.cancel_write.unwrap()
1166+
}
1167+
1168+
pub fn drop_readable(&self) -> unsafe extern "C" fn(u32) {
1169+
self.ptr.drop_readable.unwrap()
1170+
}
1171+
1172+
pub fn drop_writable(&self) -> unsafe extern "C" fn(u32) {
1173+
self.ptr.drop_writable.unwrap()
1174+
}
1175+
1176+
pub unsafe fn lift(&self, cx: &mut impl Call, buffer: *mut u8) {
1177+
unsafe { self.ptr.lift.unwrap()((&raw mut *cx).cast(), buffer.cast()) };
1178+
}
1179+
1180+
pub unsafe fn lower(&self, cx: &mut impl Call, buffer: *mut u8) {
1181+
unsafe { self.ptr.lower.unwrap()((&raw mut *cx).cast(), buffer.cast()) };
1182+
}
1183+
1184+
pub fn abi_payload_size(&self) -> usize {
1185+
self.ptr.abi_payload_size
1186+
}
1187+
1188+
pub fn abi_payload_align(&self) -> usize {
1189+
self.ptr.abi_payload_align
1190+
}
10521191
}
10531192

10541193
impl fmt::Debug for Stream {

crates/wit-dylib/src/bindgen.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,48 @@ pub fn task_return(
425425
)
426426
}
427427

428+
pub fn lift_payload(adapter: &mut Adapter, resolve: &Resolve, ty: Type) -> Function {
429+
let mut c = FunctionCompiler::new(adapter, resolve, 2);
430+
c.ctx = Some(TempLocal::new(0, ValType::I32));
431+
let frame = StackFrame {
432+
size: 16,
433+
abi_param_offset: None,
434+
retptr_offset: None,
435+
stack_temp_offset: Some(0),
436+
};
437+
c.allocate_stack_frame(&frame);
438+
c.lift(
439+
&AbiLoc::Memory(Memory {
440+
addr: TempLocal::new(1, ValType::I32),
441+
offset: 0,
442+
}),
443+
ty,
444+
);
445+
c.deallocate_stack_frame(&frame);
446+
c.finish()
447+
}
448+
449+
pub fn lower_payload(adapter: &mut Adapter, resolve: &Resolve, ty: Type) -> Function {
450+
let mut c = FunctionCompiler::new(adapter, resolve, 2);
451+
c.ctx = Some(TempLocal::new(0, ValType::I32));
452+
let frame = StackFrame {
453+
size: 16,
454+
abi_param_offset: None,
455+
retptr_offset: None,
456+
stack_temp_offset: Some(0),
457+
};
458+
c.allocate_stack_frame(&frame);
459+
c.lower(
460+
ty,
461+
&AbiLoc::Memory(Memory {
462+
addr: TempLocal::new(1, ValType::I32),
463+
offset: 0,
464+
}),
465+
);
466+
c.deallocate_stack_frame(&frame);
467+
c.finish()
468+
}
469+
428470
struct FunctionCompiler<'a> {
429471
adapter: &'a mut Adapter,
430472
resolve: &'a Resolve,

0 commit comments

Comments
 (0)