Files
orchid/orchid-api-traits/src/helpers.rs
Lawrence Bethlenfalvy 32d6237dc5 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
2026-01-01 14:54:29 +00:00

79 lines
2.1 KiB
Rust

use std::error::Error;
use std::io;
use std::pin::{Pin, pin};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll, Wake};
use futures::{AsyncRead, AsyncReadExt, AsyncWrite};
use itertools::{Chunk, Itertools};
use crate::Encode;
pub async fn encode_enum<'a, W: AsyncWrite + ?Sized>(
mut write: Pin<&'a mut W>,
id: u8,
f: impl AsyncFnOnce(Pin<&'a mut W>) -> io::Result<()>,
) -> io::Result<()> {
id.encode(write.as_mut()).await?;
f(write).await
}
pub fn print_bytes(b: &[u8]) -> String {
(b.iter().map(|b| format!("{b:02x}")))
.chunks(4)
.into_iter()
.map(|mut c: Chunk<_>| c.join(" "))
.join(" ")
}
pub async fn read_exact<R: AsyncRead + ?Sized>(
mut read: Pin<&mut R>,
bytes: &'static [u8],
) -> io::Result<()> {
let mut data = vec![0u8; bytes.len()];
read.read_exact(&mut data).await?;
if data == bytes {
Ok(())
} else {
let msg =
format!("Wrong bytes!\nExpected: {}\nFound: {}", print_bytes(bytes), print_bytes(&data));
Err(io::Error::new(io::ErrorKind::InvalidData, msg))
}
}
pub fn enc_vec(enc: &impl Encode) -> Vec<u8> {
let mut vec = Vec::new();
enc.encode_vec(&mut vec);
vec
}
/// Raises a bool flag when called
struct FlagWaker(AtomicBool);
impl Wake for FlagWaker {
fn wake(self: Arc<Self>) { self.0.store(true, Ordering::Relaxed) }
}
pub fn spin_on<F: Future>(fut: F) -> F::Output {
let flag = AtomicBool::new(false);
let flag_waker = Arc::new(FlagWaker(flag));
let mut future = pin!(fut);
loop {
let waker = flag_waker.clone().into();
let mut ctx = Context::from_waker(&waker);
match future.as_mut().poll(&mut ctx) {
// ideally the future should return synchronously
Poll::Ready(res) => break res,
// poorly written futures may yield and immediately wake
Poll::Pending if flag_waker.0.load(Ordering::Relaxed) => (),
// there is no external event to wait for, this has to be a deadlock
Poll::Pending => panic!("Future inside spin_on cannot block"),
};
}
}
pub fn decode_err() -> io::Error { io::Error::new(io::ErrorKind::InvalidData, "Unexpected zero") }
pub fn decode_err_for(e: impl Error) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, e.to_string())
}