use std::{
collections::VecDeque,
future::Future,
net::SocketAddr,
path::Path,
pin::{Pin, pin},
process::{Command, ExitStatus, Stdio},
sync::{Arc, Mutex, MutexGuard, PoisonError, Weak},
task::{Context, Poll, Wake, Waker},
time::{Duration, Instant},
};
use async_task::Runnable;
use polling::{Events, Poller};
use zbus::runtime::{IoSource, traits};
mod connect;
mod io;
pub use io::RegisteredIoSource;
use io::Sources;
mod process;
mod task;
use task::Detached;
pub use task::Task;
mod timer;
pub use timer::Sleep;
use timer::Timers;
pub struct Runtime(Arc<Inner>);
impl Runtime {
pub fn new() -> std::io::Result<Self> {
let inner = Inner {
poller: Poller::new()?,
queue: Mutex::new(VecDeque::new()),
detached: Mutex::default(),
timers: Mutex::default(),
sources: Mutex::default(),
};
Ok(Self(Arc::new(inner)))
}
pub fn handle(&self) -> Handle {
Handle(self.0.clone())
}
pub fn run<F>(&self, future: F) -> F::Output
where
F: Future,
{
let inner = &self.0;
let waker = Waker::from(Arc::new(Notify(inner.clone())));
let mut cx = Context::from_waker(&waker);
let mut future = pin!(future);
let mut events = Events::new();
loop {
loop {
let next = lock(&inner.queue).pop_front();
match next {
Some(runnable) => {
runnable.run();
}
None => break,
}
}
let finished = {
let mut detached = lock(&inner.detached);
let (finished, running): (Vec<_>, Vec<_>) = std::mem::take(&mut *detached)
.into_iter()
.partition(|task| task.is_finished());
*detached = running;
finished
};
drop(finished);
if let Poll::Ready(output) = future.as_mut().poll(&mut cx) {
return output;
}
let timeout = self.timeout();
events.clear();
inner
.poller
.wait(&mut events, timeout)
.expect("the poller waits for readiness");
for event in events.iter() {
self.wake_source(event.key);
}
self.wake_due_timers();
}
}
pub fn pending_timers(&self) -> usize {
lock(&self.0.timers).pending.len()
}
pub fn probe(&self) -> Probe {
Probe(Arc::downgrade(&self.0))
}
fn timeout(&self) -> Option<Duration> {
if !lock(&self.0.queue).is_empty() {
return Some(Duration::ZERO);
}
let timers = lock(&self.0.timers);
let (deadline, _) = timers.pending.keys().next()?;
Some(deadline.saturating_duration_since(Instant::now()))
}
}
impl Drop for Runtime {
fn drop(&mut self) {
loop {
let queued = std::mem::take(&mut *lock(&self.0.queue));
let detached = std::mem::take(&mut *lock(&self.0.detached));
let timers = std::mem::take(&mut lock(&self.0.timers).pending);
let states: Vec<_> = lock(&self.0.sources).states.values().cloned().collect();
let wakers: Vec<_> = states
.iter()
.flat_map(|state| {
let mut wakers = lock(&state.wakers);
[wakers.readable.take(), wakers.writable.take()]
})
.flatten()
.collect();
if queued.is_empty() && detached.is_empty() && timers.is_empty() && wakers.is_empty() {
break;
}
drop(wakers);
drop(timers);
drop(detached);
drop(queued);
}
}
}
pub struct Probe(Weak<Inner>);
impl Probe {
pub fn is_released(&self) -> bool {
self.0.strong_count() == 0
}
}
#[derive(Clone)]
pub struct Handle(Arc<Inner>);
impl traits::Runtime for Handle {
type RegisteredIoSource = RegisteredIoSource;
type Sleep = Sleep;
type Task<T>
= Task<T>
where
T: Send + 'static;
fn register_io_source(&self, source: IoSource) -> std::io::Result<RegisteredIoSource> {
RegisteredIoSource::new(&self.0, source)
}
fn sleep(&self, duration: Duration) -> Sleep {
Sleep::new(self.0.clone(), duration)
}
fn spawn<T>(&self, _name: &str, future: impl Future<Output = T> + Send + 'static) -> Task<T>
where
T: Send + 'static,
{
let inner = Arc::downgrade(&self.0);
let schedule = move |runnable: Runnable| {
let Some(inner) = inner.upgrade() else {
return;
};
lock(&inner.queue).push_back(runnable);
let _ = inner.poller.notify();
};
let (runnable, task) = async_task::spawn(future, schedule);
runnable.schedule();
Task::new(task, Arc::downgrade(&self.0))
}
fn connect_tcp(
&self,
address: SocketAddr,
) -> impl Future<Output = std::io::Result<IoSource>> + Send {
connect::tcp(self, address)
}
fn connect_unix(&self, path: &Path) -> impl Future<Output = std::io::Result<IoSource>> + Send {
connect::unix(self, path)
}
fn spawn_process(
&self,
command: Command,
stdin: Stdio,
stdout: Stdio,
stderr: Stdio,
) -> std::io::Result<Pin<Box<dyn Future<Output = std::io::Result<ExitStatus>> + Send>>> {
process::spawn(self, command, stdin, stdout, stderr)
}
}
struct Inner {
poller: Poller,
queue: Mutex<VecDeque<Runnable>>,
detached: Mutex<Vec<Box<dyn Detached>>>,
timers: Mutex<Timers>,
sources: Mutex<Sources>,
}
struct Notify(Arc<Inner>);
impl Wake for Notify {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
let _ = self.0.poller.notify();
}
}
fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
mutex.lock().unwrap_or_else(PoisonError::into_inner)
}