This commit is contained in:
2026-03-20 15:25:46 -04:00
parent 15813ebb6c
commit 9ab1167fef
13 changed files with 711 additions and 12 deletions

View File

@@ -25,6 +25,14 @@ compile_error!("ruin-runtime currently supports only Linux x86_64.");
extern crate alloc;
pub(crate) mod trace_targets {
pub const DRIVER: &str = "ruin_runtime::driver";
pub const RUNTIME: &str = "ruin_runtime::runtime";
pub const SCHEDULER: &str = "ruin_runtime::scheduler";
pub const TIMER: &str = "ruin_runtime::timer";
pub const ASYNC: &str = "ruin_runtime::async";
}
pub mod channel;
pub mod fs;
pub mod net;

View File

@@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use super::uring::{IORING_OP_ASYNC_CANCEL, IoUring, IoUringCqe, IoUringSqe};
use crate::trace_targets;
const WAKE_TARGET_TOKEN: u64 = 1;
const TOKEN_KIND_SHIFT: u64 = 56;
@@ -34,6 +35,13 @@ struct NotifierInner {
impl NotifierInner {
fn notify(&self) -> io::Result<()> {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::DRIVER,
event = "notify",
ring_fd = self.ring_fd,
"sending cross-thread driver wake"
);
if self.closed.load(Ordering::Acquire) {
return Err(io::Error::new(
io::ErrorKind::BrokenPipe,
@@ -96,6 +104,12 @@ pub fn create() -> io::Result<(Driver, ThreadNotifier)> {
/// emphasize driver construction.
pub fn create_driver() -> io::Result<(Driver, ThreadNotifier)> {
let ring = IoUring::new(64)?;
tracing::debug!(
target: trace_targets::DRIVER,
event = "create_driver",
ring_fd = ring.ring_fd(),
"created runtime driver"
);
let notifier = Arc::new(NotifierInner {
ring_fd: ring.ring_fd(),
closed: AtomicBool::new(false),
@@ -130,11 +144,27 @@ impl Driver {
let saw_any = self
.ring
.drain_completions(|cqe| self.process_cqe(cqe, &mut ready));
#[cfg(debug_assertions)]
if saw_any {
tracing::trace!(
target: trace_targets::DRIVER,
event = "poll_ready",
timer_ready = ready.timer,
wake_ready = ready.wake,
"driver poll produced ready events"
);
}
if saw_any { Ok(Some(ready)) } else { Ok(None) }
}
/// Blocks until at least one completion is available.
pub fn wait(&self) -> io::Result<()> {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::DRIVER,
event = "wait",
"waiting for driver completion"
);
self.ring.wait_for_cqe()
}
@@ -142,6 +172,13 @@ impl Driver {
///
/// Passing `None` removes any active timer.
pub fn rearm_timer(&self, deadline: Option<Duration>) -> io::Result<()> {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::TIMER,
event = "rearm_timer",
deadline_ns = deadline.map(|value| value.as_nanos() as u64),
"rearming driver timer"
);
match (self.active_timer_token.get(), deadline) {
(Some(active), Some(deadline)) => {
self.ring.submit_timeout_update(active, deadline)?;
@@ -168,6 +205,13 @@ impl Driver {
on_complete: impl FnOnce(IoUringCqe) + Send + 'static,
) -> io::Result<u64> {
let token = self.next_token(CompletionKind::Operation);
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::ASYNC,
event = "submit_operation",
token,
"submitting async driver operation"
);
self.completions
.borrow_mut()
.insert(token, Box::new(on_complete));
@@ -181,6 +225,13 @@ impl Driver {
}
pub(crate) fn cancel_operation(&self, token: u64) -> io::Result<()> {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::ASYNC,
event = "cancel_operation",
token,
"submitting async driver cancellation"
);
self.ring
.submit_with_token(self.next_token(CompletionKind::OperationCancel), |sqe| {
sqe.opcode = IORING_OP_ASYNC_CANCEL;
@@ -216,6 +267,14 @@ impl Driver {
}
fn process_cqe(&self, cqe: IoUringCqe, ready: &mut ReadyEvents) {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::DRIVER,
event = "process_cqe",
user_data = cqe.user_data,
result = cqe.res,
"processing io_uring completion"
);
if cqe.user_data == WAKE_TARGET_TOKEN {
ready.wake = true;
let wakes = cqe.res.max(1) as u64;
@@ -256,6 +315,11 @@ impl Driver {
impl Drop for Driver {
fn drop(&mut self) {
tracing::debug!(
target: trace_targets::DRIVER,
event = "drop_driver",
"dropping runtime driver"
);
self.notifier.closed.store(true, Ordering::Release);
}
}

View File

@@ -12,6 +12,7 @@ use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use std::time::Duration;
use super::driver::{Driver, ThreadNotifier, create_driver, monotonic_now};
use crate::trace_targets;
type LocalTask = Box<dyn FnOnce() + 'static>;
type SendTask = Box<dyn FnOnce() + Send + 'static>;
@@ -95,6 +96,13 @@ pub fn queue_task<F>(task: F)
where
F: FnOnce() + 'static,
{
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::SCHEDULER,
event = "queue_task",
queue = "local_macro",
"queueing local macrotask"
);
push_local_macrotask(Box::new(task));
}
@@ -110,6 +118,13 @@ pub fn queue_microtask<F>(task: F)
where
F: FnOnce() + 'static,
{
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::SCHEDULER,
event = "queue_microtask",
queue = "local_micro",
"queueing local microtask"
);
current_thread()
.local_microtasks
.borrow_mut()
@@ -128,6 +143,15 @@ where
let owner = current_thread_ptr();
let id = allocate_timer_id();
let deadline = deadline_from_now(delay);
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::TIMER,
event = "set_timeout",
timer_id = id,
delay_ns = delay.as_nanos() as u64,
deadline_ns = deadline.as_nanos() as u64,
"scheduling timeout"
);
let timer = TimerNode::timeout(id, deadline, Box::new(callback));
current_thread().timers.borrow_mut().insert(timer);
@@ -146,6 +170,13 @@ where
///
/// Panics if called from a different runtime thread than the one that created `handle`.
pub fn clear_timeout(handle: &TimeoutHandle) {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::TIMER,
event = "clear_timeout",
timer_id = handle.id,
"clearing timeout"
);
clear_timer(handle.owner, handle.id);
}
@@ -163,6 +194,15 @@ where
let owner = current_thread_ptr();
let id = allocate_timer_id();
let deadline = deadline_from_now(delay);
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::TIMER,
event = "set_interval",
timer_id = id,
delay_ns = delay.as_nanos() as u64,
deadline_ns = deadline.as_nanos() as u64,
"scheduling interval"
);
let timer = TimerNode::interval(
id,
deadline,
@@ -186,6 +226,13 @@ where
///
/// Panics if called from a different runtime thread than the one that created `handle`.
pub fn clear_interval(handle: &IntervalHandle) {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::TIMER,
event = "clear_interval",
timer_id = handle.id,
"clearing interval"
);
clear_timer(handle.owner, handle.id);
}
@@ -212,6 +259,12 @@ where
F: Future + 'static,
F::Output: 'static,
{
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::ASYNC,
event = "queue_future",
"queueing local future"
);
let state = Rc::new(JoinState::new());
let completion = Rc::clone(&state);
let task = Rc::new(FutureTask {
@@ -240,6 +293,11 @@ where
Init: FnOnce() + Send + 'static,
Exit: FnOnce() + 'static,
{
tracing::debug!(
target: trace_targets::RUNTIME,
event = "spawn_worker",
"spawning runtime worker thread"
);
let parent = current_thread();
let (driver, notifier) = create_driver().expect("worker driver should initialize");
let shared = Arc::new(ThreadShared::new(notifier));
@@ -281,6 +339,16 @@ where
///
/// Panics if runtime initialization fails or if the underlying driver returns an unexpected error.
pub fn run() {
let _span = tracing::debug_span!(
target: trace_targets::RUNTIME,
"runtime.run"
)
.entered();
tracing::debug!(
target: trace_targets::RUNTIME,
event = "run_enter",
"entering runtime event loop"
);
let _ = current_thread();
loop {
@@ -316,6 +384,15 @@ pub fn run() {
if has_pending_timers() || state.has_live_children() || state.has_live_async_operations() {
state.shared.closing.store(false, Ordering::Release);
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::RUNTIME,
event = "run_wait",
pending_timers = has_pending_timers(),
live_children = state.has_live_children(),
live_async = state.has_live_async_operations(),
"runtime waiting for more work"
);
state.driver.wait().expect("driver wait should succeed");
continue;
}
@@ -327,6 +404,11 @@ pub fn run() {
state.shared.closed.store(true, Ordering::Release);
state.shared.notify();
tracing::debug!(
target: trace_targets::RUNTIME,
event = "run_exit",
"runtime event loop exiting"
);
teardown_thread();
return;
}
@@ -351,7 +433,16 @@ impl ThreadHandle {
where
F: FnOnce() + Send + 'static,
{
self.shared.enqueue_macro(Box::new(task))
let queued = self.shared.enqueue_macro(Box::new(task));
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::SCHEDULER,
event = "queue_remote_task",
queue = "remote_macro",
queued,
"queueing remote macrotask"
);
queued
}
/// Queues a microtask onto this runtime thread.
@@ -361,7 +452,16 @@ impl ThreadHandle {
where
F: FnOnce() + Send + 'static,
{
self.shared.enqueue_micro(Box::new(task))
let queued = self.shared.enqueue_micro(Box::new(task));
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::SCHEDULER,
event = "queue_remote_microtask",
queue = "remote_micro",
queued,
"queueing remote microtask"
);
queued
}
/// Returns `true` if the target runtime thread has shut down.
@@ -859,12 +959,24 @@ fn drain_driver_events() {
let state = current_thread();
if ready.wake {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::DRIVER,
event = "drain_wake",
"draining driver wake notifications"
);
let _ = state
.driver
.drain_wake()
.expect("wake drain should succeed");
}
if ready.timer {
#[cfg(debug_assertions)]
tracing::trace!(
target: trace_targets::TIMER,
event = "drain_timer",
"draining expired runtime timers"
);
let _ = state
.driver
.drain_timer()