partway towards commands

I got very confused and started mucking about with "spawn" when in fact all I needed was the "inline" extension type in orcx that allows the interpreter to expose custom constants.
This commit is contained in:
2026-03-13 16:48:42 +01:00
parent cdcca694c5
commit 09cfcb1839
146 changed files with 3582 additions and 2822 deletions

View File

@@ -1,18 +1,19 @@
use std::io;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use futures::io::BufReader;
use futures::{AsyncBufReadExt, StreamExt};
use hashbrown::HashMap;
use libloading::{Library, Symbol};
use orchid_base::binary::vt_to_future;
use orchid_base::logging::log;
use orchid_base::{log, on_drop, vt_to_future};
use unsync_pipe::pipe;
use crate::api;
use crate::ctx::Ctx;
use crate::extension::ExtPort;
use crate::task_set::TaskSet;
static DYNAMIC_LIBRARIES: Mutex<Option<HashMap<PathBuf, Arc<Library>>>> = Mutex::new(None);
fn load_dylib(path: &Path) -> Result<Arc<Library>, libloading::Error> {
@@ -32,7 +33,7 @@ pub async fn ext_dylib(path: &Path, ctx: Ctx) -> Result<ExtPort, libloading::Err
let (output, read_output) = pipe(1024);
let (write_log, read_log) = pipe(1024);
let log_path = path.to_string_lossy().to_string();
let _ = ctx.spawn(async move {
let _ = ctx.spawn(Duration::ZERO, async move {
let mut lines = BufReader::new(read_log).lines();
while let Some(line) = lines.next().await {
match line {
@@ -44,16 +45,43 @@ pub async fn ext_dylib(path: &Path, ctx: Ctx) -> Result<ExtPort, libloading::Err
}
}
});
let tasks = TaskSet::default();
let library = load_dylib(path)?;
let entrypoint: Symbol<unsafe extern "C" fn(api::binary::ExtensionContext)> =
unsafe { library.get("orchid_extension_main") }?;
let data = Box::into_raw(Box::new(ctx)) as *const ();
extern "C" fn drop(data: *const ()) { std::mem::drop(unsafe { Box::from_raw(data as *mut Ctx) }) }
extern "C" fn spawn(data: *const (), vt: api::binary::FutureBin) {
let _ = unsafe { (data as *mut Ctx).as_mut().unwrap().spawn(vt_to_future(vt)) };
}
let data = Box::into_raw(Box::new(SpawnerState { ctx, tasks: tasks.clone() })) as *const ();
let spawner = api::binary::SpawnerBin { data, drop, spawn };
let cx = api::binary::ExtensionContext { input, output, log: write_log, spawner };
unsafe { (entrypoint)(cx) };
Ok(ExtPort { input: Box::pin(write_input), output: Box::pin(read_output) })
Ok(ExtPort {
input: Box::pin(write_input),
output: Box::pin(read_output),
drop_trigger: Box::new(on_drop(move || tasks.abort_all())),
})
}
struct SpawnerState {
ctx: Ctx,
tasks: TaskSet,
}
extern "C" fn drop(data: *const ()) {
let state = unsafe { Box::from_raw(data as *mut SpawnerState) };
state.tasks.abort_all();
}
extern "C" fn spawn(data: *const (), delay: u64, vt: api::binary::FutureBin) {
let future = vt_to_future(vt);
// SAFETY: this is technically a Box but it can be used directly as a &mut
let state = unsafe { (data as *mut SpawnerState).as_mut() }.unwrap();
state.tasks.with(|store| {
store.add_with(|id| {
state.ctx.spawn(Duration::from_millis(delay), async move {
future.await;
// SAFETY: We know this is live because the drop handle that frees it also
// aborts the future
let state = unsafe { (data as *mut SpawnerState).as_mut() }.unwrap();
state.tasks.with(|store| store.remove(id));
})
});
});
}