Traditional route appears to work
Beginnings of dylib extensions, entirely untestted
This commit is contained in:
@@ -6,7 +6,7 @@ edition = "2024"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ordered-float = "5.0.0"
|
||||
ordered-float = "5.1.0"
|
||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
||||
@@ -14,4 +14,4 @@ itertools = "0.14.0"
|
||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||
|
||||
[dev-dependencies]
|
||||
test_executors = "0.3.5"
|
||||
test_executors = "0.4.1"
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
|
||||
use crate::ExtHostNotif;
|
||||
use crate::{ExtHostNotif, TStr};
|
||||
|
||||
/// Describes what to do with a log stream.
|
||||
/// Log streams are unstructured utf8 text unless otherwise stated.
|
||||
#[derive(Clone, Debug, Coding, PartialEq, Eq, Hash)]
|
||||
pub enum LogStrategy {
|
||||
StdErr,
|
||||
File(String),
|
||||
/// Context-dependent default stream, often stderr
|
||||
Default,
|
||||
/// A file on the local filesystem
|
||||
File { path: String, append: bool },
|
||||
/// Discard any log output
|
||||
Discard,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding)]
|
||||
pub struct Logger {
|
||||
pub routing: HashMap<String, LogStrategy>,
|
||||
pub default: Option<LogStrategy>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||
#[extends(ExtHostNotif)]
|
||||
pub struct Log(pub String);
|
||||
pub struct Log {
|
||||
pub category: TStr,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
@@ -34,23 +34,18 @@ use crate::{Sweeped, atom, expr, interner, lexer, logging, parser, system, tree}
|
||||
static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n";
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HostHeader {
|
||||
pub log_strategy: logging::LogStrategy,
|
||||
pub msg_logs: logging::LogStrategy,
|
||||
pub logger: logging::Logger,
|
||||
}
|
||||
impl Decode for HostHeader {
|
||||
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||
read_exact(read.as_mut(), HOST_INTRO).await?;
|
||||
Ok(Self {
|
||||
log_strategy: logging::LogStrategy::decode(read.as_mut()).await?,
|
||||
msg_logs: logging::LogStrategy::decode(read.as_mut()).await?,
|
||||
})
|
||||
Ok(Self { logger: logging::Logger::decode(read).await? })
|
||||
}
|
||||
}
|
||||
impl Encode for HostHeader {
|
||||
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) -> io::Result<()> {
|
||||
write.write_all(HOST_INTRO).await?;
|
||||
self.log_strategy.encode(write.as_mut()).await?;
|
||||
self.msg_logs.encode(write.as_mut()).await
|
||||
self.logger.encode(write.as_mut()).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,19 +154,19 @@ impl MsgSet for HostMsgSet {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use orchid_api_traits::enc_vec;
|
||||
use ordered_float::NotNan;
|
||||
use test_executors::spin_on;
|
||||
|
||||
use super::*;
|
||||
use crate::Logger;
|
||||
|
||||
#[test]
|
||||
fn host_header_enc() {
|
||||
spin_on(async {
|
||||
let hh = HostHeader {
|
||||
log_strategy: logging::LogStrategy::File("SomeFile".to_string()),
|
||||
msg_logs: logging::LogStrategy::File("SomeFile".to_string()),
|
||||
};
|
||||
let hh = HostHeader { logger: Logger { routing: HashMap::new(), default: None } };
|
||||
let mut enc = &enc_vec(&hh)[..];
|
||||
eprintln!("Encoded to {enc:?}");
|
||||
HostHeader::decode(Pin::new(&mut enc)).await.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user