task_local context over context objects
- interner impls logically separate from API in orchid-base (default host interner still in base for testing) - error reporting, logging, and a variety of other features passed down via context in extension, not yet in host to maintain library-ish profile, should consider options - no global spawn mechanic, the host has a spawn function but extensions only get a stash for enqueuing async work in sync callbacks which is then explicitly, manually, and with strict order popped and awaited - still deadlocks nondeterministically for some ungodly reason
This commit is contained in:
@@ -1,57 +1,24 @@
|
||||
use crate::entrypoint::ExtensionData;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::entrypoint::ExtensionBuilder;
|
||||
use crate::ext_port::ExtPort;
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub async fn tokio_main(data: ExtensionData) {
|
||||
use std::io::{ErrorKind, Write};
|
||||
use std::mem;
|
||||
use std::pin::{Pin, pin};
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use futures::StreamExt;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::lock::Mutex;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use orchid_api_traits::{Decode, Encode};
|
||||
use orchid_base::msg::{recv_msg, send_msg};
|
||||
use tokio::io::{Stdout, stdin, stdout};
|
||||
pub async fn tokio_main(builder: ExtensionBuilder) {
|
||||
use tokio::io::{stderr, stdin, stdout};
|
||||
use tokio::task::{LocalSet, spawn_local};
|
||||
use tokio_util::compat::{Compat, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
|
||||
use crate::api;
|
||||
use crate::entrypoint::extension_init;
|
||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
|
||||
let local_set = LocalSet::new();
|
||||
local_set.spawn_local(async {
|
||||
let host_header = api::HostHeader::decode(Pin::new(&mut stdin().compat())).await;
|
||||
let init =
|
||||
Rc::new(extension_init(data, host_header, Rc::new(|fut| mem::drop(spawn_local(fut)))));
|
||||
let mut buf = Vec::new();
|
||||
init.header.encode(Pin::new(&mut buf)).await;
|
||||
std::io::stdout().write_all(&buf).unwrap();
|
||||
std::io::stdout().flush().unwrap();
|
||||
// These are concurrent processes that never exit, so if the FuturesUnordered
|
||||
// produces any result the extension should exit
|
||||
let mut io = FuturesUnordered::<LocalBoxFuture<()>>::new();
|
||||
io.push(Box::pin(async {
|
||||
loop {
|
||||
match recv_msg(pin!(stdin().compat())).await {
|
||||
Ok(msg) => init.send(&msg[..]).await,
|
||||
Err(e) if e.kind() == ErrorKind::BrokenPipe => break,
|
||||
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
|
||||
Err(e) => panic!("{e}"),
|
||||
}
|
||||
}
|
||||
}));
|
||||
io.push(Box::pin(async {
|
||||
while let Some(msg) = init.recv().await {
|
||||
static STDOUT: OnceCell<Mutex<Compat<Stdout>>> = OnceCell::new();
|
||||
let stdout_lk = STDOUT.get_or_init(async { Mutex::new(stdout().compat_write()) }).await;
|
||||
let mut stdout_g = stdout_lk.lock().await;
|
||||
send_msg(pin!(&mut *stdout_g), &msg[..]).await.expect("Parent pipe broken");
|
||||
}
|
||||
}));
|
||||
io.next().await;
|
||||
builder.build(ExtPort {
|
||||
input: Box::pin(stdin().compat()),
|
||||
output: Box::pin(stdout().compat_write()),
|
||||
log: Box::pin(stderr().compat_write()),
|
||||
spawn: Rc::new(|fut| {
|
||||
spawn_local(fut);
|
||||
}),
|
||||
});
|
||||
});
|
||||
local_set.await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user