#![cfg_attr(feature = "runtime", doc = include_str!("../README.md"))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/z-galaxy/zruntime/f7ba9e0ca073c3d2981bbb443a74ad62a863255e/data/logo.png"
)]
#![cfg_attr(
all(feature = "event", not(feature = "runtime")),
doc = include_str!("event-only.md")
)]
#![deny(rust_2018_idioms)]
#![doc(test(attr(
warn(unused),
deny(warnings),
allow(dead_code),
// W/o this, we seem to get some bogus warning about `extern crate zbus`.
allow(unused_extern_crates),
)))]
#[cfg(feature = "runtime")]
mod async_io;
#[cfg(feature = "broadcast")]
pub mod broadcast;
#[cfg(any(feature = "runtime", feature = "fs"))]
mod coop;
#[cfg(feature = "helper")]
mod driver;
#[cfg(feature = "event")]
mod event;
#[cfg(feature = "fs")]
pub mod fs;
#[cfg(feature = "lock")]
pub mod lock;
#[cfg(feature = "runtime")]
mod log;
#[cfg(feature = "runtime")]
mod mode;
#[cfg(feature = "mpmc")]
pub mod mpmc;
#[cfg(any(feature = "tcp", feature = "udp", all(feature = "unix", unix)))]
pub mod net;
#[cfg(feature = "runtime")]
mod poll;
#[cfg(all(feature = "process", not(target_os = "wasi")))]
pub mod process;
#[cfg(feature = "runtime")]
mod reactor;
#[cfg(feature = "runtime")]
mod runtime;
#[cfg(feature = "runtime")]
mod scheduler;
#[cfg(feature = "runtime")]
mod time;
#[cfg(all(feature = "unblock", not(target_os = "wasi")))]
mod unblock;
#[cfg(all(feature = "runtime", any(unix, target_os = "wasi")))]
use std::os::fd::AsFd as AsSource;
#[cfg(all(feature = "runtime", windows))]
use std::os::windows::io::AsSocket as AsSource;
#[cfg(feature = "runtime")]
use std::{
borrow::Cow,
fmt,
future::{Future, IntoFuture},
io,
panic::Location,
pin::Pin,
rc::Rc,
sync::Arc,
task::{Context, Poll},
time::{Duration, Instant},
};
#[cfg(feature = "runtime")]
pub use async_io::AsyncIo;
#[cfg(feature = "runtime")]
pub use coop::unconstrained;
#[cfg(feature = "event")]
pub use event::{Event, EventListener};
#[cfg(feature = "runtime")]
use mode::sealed::Sealed as _;
#[cfg(feature = "runtime")]
pub use mode::{Local, Mode, Shared, Source};
#[cfg(feature = "runtime")]
pub use reactor::{Readiness, Registration};
#[cfg(feature = "runtime")]
use runtime::Core;
#[cfg(feature = "runtime")]
use scheduler::{JoinHandle, Name};
#[cfg(feature = "runtime")]
pub use time::{Interval, MissedTickBehavior, Sleep, TimedOut, Timeout};
#[cfg(all(feature = "unblock", not(target_os = "wasi")))]
pub use unblock::{BlockingWork, Unblock, unblock};
#[cfg(feature = "helper")]
pub fn block_on<F>(future: F) -> F::Output
where
F: Future,
{
driver::block_on(driver::Target::Own, &driver::own, future)
}
#[cfg(feature = "runtime")]
#[track_caller]
pub fn spawn<F>(future: F) -> Task<F::Output, Shared>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let name = Name::SpawnedAt(Location::caller());
spawn_on_shared(&shared_to_spawn_on(), name, future)
}
#[cfg(feature = "runtime")]
#[track_caller]
pub fn spawn_local<F>(future: F) -> Task<F::Output, Local>
where
F: Future + 'static,
F::Output: 'static,
{
let name = Name::SpawnedAt(Location::caller());
let Some(core) = Local::driven() else {
panic!(
"spawn_local called where no local runtime is running: call it from inside \
`LocalRuntime::block_on`, or from a task of that runtime"
);
};
Task(scheduler::spawn_local(&core, name, future))
}
#[cfg(feature = "runtime")]
pub type LocalRuntime = Runtime<Local>;
#[cfg(feature = "runtime")]
pub type SharedRuntime = Runtime<Shared>;
#[cfg(feature = "runtime")]
pub struct Runtime<M = Local>
where
M: Mode,
{
core: M::Ptr<Core<M>>,
}
#[cfg(feature = "runtime")]
impl<M> Runtime<M>
where
M: Mode,
{
pub fn new() -> io::Result<Self> {
Ok(Self {
core: Core::<M>::new()?,
})
}
pub fn block_on<F>(&self, future: F) -> F::Output
where
F: Future,
{
M::block_on(&self.core, future)
}
pub fn sleep(&self, duration: Duration) -> Sleep<M> {
let deadline = Instant::now().checked_add(duration);
Sleep(reactor::sleep::<M>(&self.core, deadline))
}
pub fn sleep_until(&self, deadline: Instant) -> Sleep<M> {
Sleep(reactor::sleep::<M>(&self.core, Some(deadline)))
}
pub fn timeout<F>(&self, duration: Duration, future: F) -> Timeout<F::IntoFuture, M>
where
F: IntoFuture,
{
Timeout::new(future.into_future(), self.sleep(duration))
}
pub fn timeout_at<F>(&self, deadline: Instant, future: F) -> Timeout<F::IntoFuture, M>
where
F: IntoFuture,
{
Timeout::new(future.into_future(), self.sleep_until(deadline))
}
pub fn interval(&self, period: Duration) -> Interval<M> {
Interval::new(self.sleep(period), period)
}
pub fn interval_at(&self, start: Instant, period: Duration) -> Interval<M> {
Interval::new(self.sleep_until(start), period)
}
}
#[cfg(feature = "runtime")]
impl Runtime<Local> {
pub fn spawn<T>(
&self,
name: impl Into<Cow<'static, str>>,
future: impl Future<Output = T> + 'static,
) -> Task<T, Local>
where
T: 'static,
{
Task(scheduler::spawn_local(
&self.core,
Name::Given(name.into()),
future,
))
}
pub fn register<S>(&self, source: S) -> io::Result<Registration<Local>>
where
S: AsSource + 'static,
{
reactor::register::<Local>(&self.core, Rc::new(source))
}
}
#[cfg(feature = "runtime")]
impl Runtime<Shared> {
#[cfg(feature = "helper")]
pub fn current() -> io::Result<Self> {
Ok(Self {
core: driver::current()?,
})
}
pub fn spawn<T>(
&self,
name: impl Into<Cow<'static, str>>,
future: impl Future<Output = T> + Send + 'static,
) -> Task<T, Shared>
where
T: Send + 'static,
{
spawn_on_shared(&self.core, Name::Given(name.into()), future)
}
pub fn register<S>(&self, source: S) -> io::Result<Registration<Shared>>
where
S: AsSource + Send + Sync + 'static,
{
let registered = reactor::register::<Shared>(&self.core, Arc::new(source))?;
self.core.ensure_progress();
Ok(registered)
}
#[cfg(all(test, feature = "helper"))]
pub(crate) fn from_inner(core: Arc<Core<Shared>>) -> Self {
Self { core }
}
#[cfg(all(test, feature = "helper"))]
pub(crate) fn inner(&self) -> &Arc<Core<Shared>> {
&self.core
}
#[cfg(all(test, feature = "helper"))]
pub(crate) fn helper_running(&self) -> bool {
driver::seat(&self.core).helper_running()
}
#[cfg(all(test, feature = "helper", feature = "event"))]
pub(crate) fn helper_parked(&self) -> bool {
driver::seat(&self.core).helper_parked()
}
}
#[cfg(feature = "runtime")]
impl<M> Clone for Runtime<M>
where
M: Mode,
{
fn clone(&self) -> Self {
Self {
core: self.core.clone(),
}
}
}
#[cfg(feature = "runtime")]
impl<M> fmt::Debug for Runtime<M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Runtime").finish_non_exhaustive()
}
}
#[cfg(feature = "runtime")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Interest {
Readable,
Writable,
}
#[cfg(feature = "runtime")]
pub struct Task<T, M = Local>(JoinHandle<T, M>)
where
M: Mode;
#[cfg(feature = "runtime")]
impl<T, M> Task<T, M>
where
M: Mode,
{
pub fn detach(self) {
self.0.detach();
}
pub fn cancel(self) -> impl Future<Output = Option<T>> {
self.0.cancel()
}
pub fn is_finished(&self) -> bool {
self.0.is_finished()
}
}
#[cfg(feature = "runtime")]
impl<T, M> fmt::Debug for Task<T, M>
where
M: Mode,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Task").field(&self.0).finish()
}
}
#[cfg(feature = "runtime")]
impl<T, M> Future for Task<T, M>
where
M: Mode,
{
type Output = io::Result<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.0.poll_join(cx)
}
}
#[cfg(feature = "runtime")]
fn spawn_on_shared<F>(core: &Arc<Core<Shared>>, name: Name, future: F) -> Task<F::Output, Shared>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let task = Task(scheduler::spawn_shared(core, name, future));
core.ensure_progress();
task
}
#[cfg(feature = "runtime")]
#[track_caller]
fn shared_to_spawn_on() -> Arc<Core<Shared>> {
if let Some(core) = Shared::driven() {
return core;
}
#[cfg(feature = "helper")]
return match driver::current_to_spawn_on() {
Ok(core) => core,
Err(error) => panic!("spawn found no runtime to spawn on, and could not make one: {error}"),
};
#[cfg(not(feature = "helper"))]
panic!(
"spawn called where no shared runtime is running: call it from inside \
`SharedRuntime::block_on`, or from a task of that runtime, or enable the `helper` feature"
);
}
#[cfg(any(test, doctest))]
mod tests;