Traditional route appears to work

Beginnings of dylib extensions, entirely untestted
This commit is contained in:
2026-01-12 01:38:10 +01:00
parent 32d6237dc5
commit 1a7230ce9b
40 changed files with 1560 additions and 1135 deletions

View File

@@ -1,32 +1,46 @@
//! # Binary extension definition
//!
//! A binary extension is a DLL / shared object / dylib with a symbol called
//! `orchid_extension_main` which accepts a single argument of type [ExtCtx].
//! Once that is received, communication continuees through the channel with the
//! same protocol outlined in [crate::proto]
//! `orchid_extension_main` which accepts a single argument of type
//! [ExtensionContext]. Once that is received, communication continuees through
//! the channel with the same protocol outlined in [crate::proto]
use unsync_pipe::{Reader, Writer};
/// !Send !Sync owned waker
///
/// This object is [Clone] for convenience but it has `drop` and no `clone` so
/// interactions must reflect a single logical owner
#[derive(Clone, Copy)]
#[repr(C)]
pub struct OwnedWakerVT {
data: *const (),
pub data: *const (),
/// `self`
drop: extern "C" fn(*const ()),
pub drop: extern "C" fn(*const ()),
/// `self`
pub wake: extern "C" fn(*const ()),
/// `&self`
wake: extern "C" fn(*const ()),
pub wake_ref: extern "C" fn(*const ()),
}
/// !Send !Sync, equivalent to `&mut Context<'a>`, hence no `drop`.
/// When received in [FutureVT::poll], it must not outlive the call.
///
/// You cannot directly wake using this waker, because such a trampoline would
/// pass through the binary interface twice for no reason. An efficient
/// implementation should implement that trampoline action internally, whereas
/// an inefficient but compliant implementation can clone a fresh waker and use
/// it up.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct FutureContextVT {
data: *const (),
pub data: *const (),
/// `&self`
waker: extern "C" fn(*const ()) -> OwnedWakerVT,
pub waker: extern "C" fn(*const ()) -> OwnedWakerVT,
}
/// ABI-stable `Poll<()>`
#[derive(Clone, Copy)]
#[repr(C)]
pub enum UnitPoll {
Pending,
@@ -34,34 +48,43 @@ pub enum UnitPoll {
}
/// ABI-stable `Pin<Box<dyn Future<Output = ()>>>`
///
/// This object is [Clone] for convenience, but it has `drop` and no `clone` so
/// interactions must reflect a single logical owner
#[derive(Clone, Copy)]
#[repr(C)]
pub struct FutureVT {
data: *const (),
pub data: *const (),
/// `self`
drop: extern "C" fn(*const ()),
pub drop: extern "C" fn(*const ()),
/// `&mut self` Equivalent to [Future::poll]
poll: extern "C" fn(*const (), FutureContextVT) -> UnitPoll,
pub poll: extern "C" fn(*const (), FutureContextVT) -> UnitPoll,
}
/// Owned extension context.
/// Handle for a runtime that allows its holder to spawn futures across dynamic
/// library boundaries
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Spawner {
pub data: *const (),
/// `self`
pub drop: extern "C" fn(*const ()),
/// `&self` Add a future to this extension's task
pub spawn: extern "C" fn(*const (), FutureVT),
}
/// Extension context.
///
/// When an extension starts, this is passed to
/// This struct is a plain old value, all of the contained values have a
/// distinct `drop` member
#[repr(C)]
pub struct ExtensionContext {
data: *const (),
/// `self`
drop: extern "C" fn(*const ()),
/// `self` upgrade to a later version of this struct. May only be called if
/// none of the other elements have been used yet. If a newer version isn't
/// supported, the server must return null, otherwise the the return value is
/// a pointer to the immediate next version of this struct
next: extern "C" fn(*const ()) -> *const (),
/// `&self` Add a future to this extension's task
spawn: extern "C" fn(*const (), FutureVT),
/// Spawns tasks associated with this extension
pub spawner: Spawner,
/// serialized [crate::HostExtChannel]
input: Reader,
pub input: Reader,
/// serialized [crate::ExtHostChannel]
output: Writer,
pub output: Writer,
/// UTF-8 log stream directly to log service.
log: Writer,
pub log: Writer,
}