use std::io; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use hashbrown::HashMap; use libloading::Library; use orchid_base::binary::vt_to_future; use crate::api; use crate::ctx::Ctx; use crate::extension::ExtPort; static DYNAMIC_LIBRARIES: Mutex>>> = Mutex::new(None); fn load_dylib(path: &Path) -> Result, libloading::Error> { let mut g = DYNAMIC_LIBRARIES.lock().unwrap(); let map = g.get_or_insert_default(); if let Some(lib) = map.get(path) { Ok(lib.clone()) } else { let lib = Arc::new(unsafe { Library::new(path) }?); map.insert(path.to_owned(), lib.clone()); Ok(lib) } } #[cfg(feature = "tokio")] pub async fn ext_dylib(path: &Path, ctx: Ctx) -> Result { use futures::io::BufReader; use futures::{AsyncBufReadExt, StreamExt}; use libloading::Symbol; use unsync_pipe::pipe; let (write_input, input) = pipe(1024); let (output, read_output) = pipe(1024); let (log, read_log) = pipe(1024); let log_path = path.to_string_lossy().to_string(); let _ = ctx.spawn(async move { use orchid_base::logging::log; let mut lines = BufReader::new(read_log).lines(); while let Some(line) = lines.next().await { match line { Ok(line) => writeln!(log("stderr"), "{log_path} err> {line}").await, Err(e) => match e.kind() { io::ErrorKind::BrokenPipe | io::ErrorKind::UnexpectedEof => break, _ => panic!("Error while reading stderr {e}"), }, } } }); let library = load_dylib(path)?; let entrypoint: Symbol = 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 spawner = api::binary::SpawnerBin { data, drop, spawn }; let cx = api::binary::ExtensionContext { input, output, log, spawner }; unsafe { (entrypoint)(cx) }; Ok(ExtPort { input: Box::pin(write_input), output: Box::pin(read_output) }) }