use std::{fmt, future::poll_fn, pin::Pin};
use futures_core::Stream;
#[cfg(unix)]
pub(crate) const FDS_MAX: usize = 1024;
pub(crate) fn padding_for_8_bytes(value: usize) -> usize {
padding_for_n_bytes(value, 8)
}
pub(crate) fn padding_for_n_bytes(value: usize, align: usize) -> usize {
let len_rounded_up = value.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
len_rounded_up.wrapping_sub(value)
}
pub(crate) fn fmt_bitflags<F>(name: &str, flags: &F, f: &mut fmt::Formatter<'_>) -> fmt::Result
where
F: bitflags::Flags,
F::Bits: bitflags::parser::WriteHex,
{
write!(f, "{name}(")?;
if flags.is_empty() {
f.write_str("0x0")?;
} else {
bitflags::parser::to_writer(flags, &mut *f)?;
}
f.write_str(")")
}
pub(crate) async fn next<S>(stream: &mut S) -> Option<S::Item>
where
S: Stream + Unpin,
{
poll_fn(|cx| Pin::new(&mut *stream).poll_next(cx)).await
}
#[doc(hidden)]
pub trait ResultAdapter {
type Ok;
type Err;
}
impl<T, E> ResultAdapter for Result<T, E> {
type Ok = T;
type Err = E;
}
#[cfg(all(feature = "default-rt", not(feature = "tokio")))]
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
crate::runtime::zruntime::block_on(future)
}
#[cfg(not(any(feature = "default-rt", feature = "tokio")))]
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
use std::{
pin::pin,
sync::{Arc, atomic::Ordering},
task::{Context, Poll, Waker},
thread,
};
let unparker = Arc::new(Unparker {
thread: thread::current(),
woken: Default::default(),
});
let waker = Waker::from(unparker.clone());
let mut cx = Context::from_waker(&waker);
let mut future = pin!(future);
loop {
if let Poll::Ready(output) = future.as_mut().poll(&mut cx) {
return output;
}
while !unparker.woken.swap(false, Ordering::Acquire) {
thread::park();
}
}
}
#[cfg(feature = "tokio")]
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
use std::sync::OnceLock;
static TOKIO_RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
TOKIO_RT
.get_or_init(|| {
#[cfg(feature = "tokio-multithread")]
let mut builder = tokio::runtime::Builder::new_multi_thread();
#[cfg(not(feature = "tokio-multithread"))]
let mut builder = tokio::runtime::Builder::new_current_thread();
builder
.enable_io()
.enable_time()
.build()
.expect("launch of tokio runtime")
})
.block_on(future)
}
#[cfg(not(any(feature = "default-rt", feature = "tokio")))]
struct Unparker {
thread: std::thread::Thread,
woken: std::sync::atomic::AtomicBool,
}
#[cfg(not(any(feature = "default-rt", feature = "tokio")))]
impl std::task::Wake for Unparker {
fn wake(self: std::sync::Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &std::sync::Arc<Self>) {
if !self.woken.swap(true, std::sync::atomic::Ordering::Release) {
self.thread.unpark();
}
}
}
#[cfg(all(test, not(any(feature = "default-rt", feature = "tokio"))))]
mod tests {
use std::{future::poll_fn, task::Poll, thread, time::Duration};
use ntest::timeout;
use super::block_on;
#[test]
#[timeout(15000)]
fn block_on_polls_again_once_another_thread_wakes_the_future() {
let event = zruntime::Event::new();
let listener = event.listen();
let notifier = thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
event.notify(1);
});
block_on(listener);
notifier.join().unwrap();
}
#[test]
#[timeout(15000)]
fn block_on_keeps_a_wake_up_the_future_parked_away() {
let mut polled = false;
block_on(poll_fn(|cx| {
if polled {
return Poll::Ready(());
}
polled = true;
let waker = cx.waker().clone();
thread::spawn(move || waker.wake()).join().unwrap();
thread::park();
Poll::Pending
}));
}
}