Tracing
This commit is contained in:
114
Cargo.lock
generated
114
Cargo.lock
generated
@@ -2,6 +2,15 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@@ -14,6 +23,12 @@ version = "1.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.32"
|
version = "0.3.32"
|
||||||
@@ -95,12 +110,39 @@ version = "1.0.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.183"
|
version = "0.2.183"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -131,6 +173,23 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruin-runtime"
|
name = "ruin-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -140,6 +199,7 @@ dependencies = [
|
|||||||
"hyper",
|
"hyper",
|
||||||
"libc",
|
"libc",
|
||||||
"ruin-runtime-proc-macros",
|
"ruin-runtime-proc-macros",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -156,6 +216,17 @@ name = "ruin_reactivity"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ruin-runtime",
|
"ruin-runtime",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -175,6 +246,15 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.50.0"
|
version = "1.50.0"
|
||||||
@@ -184,6 +264,40 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"once_cell",
|
||||||
|
"regex-automata",
|
||||||
|
"sharded-slab",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
|||||||
@@ -5,3 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ruin_runtime = { package = "ruin-runtime", path = "../runtime" }
|
ruin_runtime = { package = "ruin-runtime", path = "../runtime" }
|
||||||
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt", "std"] }
|
||||||
|
|||||||
101
lib/reactivity/examples/tracing_subscriber_showcase.rs
Normal file
101
lib/reactivity/examples/tracing_subscriber_showcase.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
//! Example tracing setup for RUIN runtime + reactivity.
|
||||||
|
//!
|
||||||
|
//! Try:
|
||||||
|
//!
|
||||||
|
//! - `cargo run -p ruin_reactivity --example tracing_subscriber_showcase`
|
||||||
|
//! - `RUST_LOG=info,ruin_runtime::runtime=debug,ruin_reactivity::graph=debug cargo run -p ruin_reactivity --example tracing_subscriber_showcase`
|
||||||
|
//! - `RUST_LOG=info,ruin_runtime::scheduler=trace,ruin_reactivity::event=trace,ruin_reactivity::effect=debug cargo run -p ruin_reactivity --example tracing_subscriber_showcase`
|
||||||
|
|
||||||
|
use ruin_reactivity::{cell, effect, event, on, thunk};
|
||||||
|
use ruin_runtime::time::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
use tracing_subscriber::util::SubscriberInitExt;
|
||||||
|
use tracing_subscriber::{EnvFilter, fmt};
|
||||||
|
|
||||||
|
fn install_tracing() {
|
||||||
|
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||||
|
EnvFilter::new(
|
||||||
|
"info,\
|
||||||
|
ruin_runtime::runtime=debug,\
|
||||||
|
ruin_runtime::scheduler=debug,\
|
||||||
|
ruin_reactivity::graph=debug,\
|
||||||
|
ruin_reactivity::effect=debug,\
|
||||||
|
ruin_reactivity::event=debug",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let fmt_layer = fmt::layer()
|
||||||
|
.with_target(true)
|
||||||
|
.with_thread_ids(true)
|
||||||
|
.with_thread_names(true)
|
||||||
|
.compact();
|
||||||
|
|
||||||
|
let _ = tracing_subscriber::registry()
|
||||||
|
.with(filter)
|
||||||
|
.with(fmt_layer)
|
||||||
|
.try_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ruin_runtime::async_main]
|
||||||
|
async fn main() {
|
||||||
|
install_tracing();
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
event = "showcase_start",
|
||||||
|
note = "override RUST_LOG to see more or less detail",
|
||||||
|
"starting tracing subscriber showcase"
|
||||||
|
);
|
||||||
|
|
||||||
|
let count = cell(0usize);
|
||||||
|
let doubled = thunk({
|
||||||
|
let count = count.clone();
|
||||||
|
move || count.get() * 2
|
||||||
|
});
|
||||||
|
let clicks = event::<usize>();
|
||||||
|
|
||||||
|
let _drain = on(&clicks, {
|
||||||
|
let count = count.clone();
|
||||||
|
move |delta| {
|
||||||
|
tracing::info!(
|
||||||
|
event = "apply_click_delta",
|
||||||
|
delta = *delta,
|
||||||
|
"draining queued event into cell update"
|
||||||
|
);
|
||||||
|
count.update(|value| *value += *delta);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let _view = effect({
|
||||||
|
let count = count.clone();
|
||||||
|
let doubled = doubled.clone();
|
||||||
|
move || {
|
||||||
|
tracing::info!(
|
||||||
|
target: "demo::view",
|
||||||
|
count = count.get(),
|
||||||
|
doubled = doubled.get(),
|
||||||
|
"derived view state updated"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
event = "emit_clicks",
|
||||||
|
count = 3,
|
||||||
|
"emitting queued click deltas"
|
||||||
|
);
|
||||||
|
clicks.emit(1);
|
||||||
|
clicks.emit(2);
|
||||||
|
clicks.emit(0);
|
||||||
|
|
||||||
|
tracing::info!(event = "manual_set", value = 10, "setting count directly");
|
||||||
|
let _ = count.set(10);
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
event = "showcase_done",
|
||||||
|
hint = "see ruin_runtime::* and ruin_reactivity::* targets in the filter",
|
||||||
|
"example body completed; awaiting once so scheduled microtasks can flush"
|
||||||
|
);
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(1)).await;
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{NodeId, Reactor, current};
|
use crate::{NodeId, Reactor, current, trace_targets};
|
||||||
|
|
||||||
/// Creates a [`Cell`] in the current thread's default reactor.
|
/// Creates a [`Cell`] in the current thread's default reactor.
|
||||||
pub fn cell<T: 'static>(initial: T) -> Cell<T> {
|
pub fn cell<T: 'static>(initial: T) -> Cell<T> {
|
||||||
@@ -29,6 +29,12 @@ impl Reactor {
|
|||||||
impl<T: 'static> Cell<T> {
|
impl<T: 'static> Cell<T> {
|
||||||
fn new(reactor: Reactor, initial: T) -> Self {
|
fn new(reactor: Reactor, initial: T) -> Self {
|
||||||
let id = reactor.allocate_node();
|
let id = reactor.allocate_node();
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::CELL,
|
||||||
|
event = "create_cell",
|
||||||
|
node_id = id.0,
|
||||||
|
"created reactive cell"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
inner: Rc::new(CellInner {
|
inner: Rc::new(CellInner {
|
||||||
reactor,
|
reactor,
|
||||||
@@ -40,6 +46,13 @@ impl<T: 'static> Cell<T> {
|
|||||||
|
|
||||||
/// Runs `f` with a shared reference to the current value.
|
/// Runs `f` with a shared reference to the current value.
|
||||||
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
|
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::CELL,
|
||||||
|
event = "read_cell",
|
||||||
|
node_id = self.inner.id.0,
|
||||||
|
"reading reactive cell"
|
||||||
|
);
|
||||||
self.inner.reactor.observe(self.inner.id);
|
self.inner.reactor.observe(self.inner.id);
|
||||||
let value = self.inner.value.borrow();
|
let value = self.inner.value.borrow();
|
||||||
f(&value)
|
f(&value)
|
||||||
@@ -48,6 +61,12 @@ impl<T: 'static> Cell<T> {
|
|||||||
/// Replaces the current value and notifies dependents.
|
/// Replaces the current value and notifies dependents.
|
||||||
pub fn replace(&self, value: T) -> T {
|
pub fn replace(&self, value: T) -> T {
|
||||||
let previous = self.inner.value.replace(value);
|
let previous = self.inner.value.replace(value);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::CELL,
|
||||||
|
event = "replace_cell",
|
||||||
|
node_id = self.inner.id.0,
|
||||||
|
"replaced cell value"
|
||||||
|
);
|
||||||
self.inner.reactor.trigger(self.inner.id);
|
self.inner.reactor.trigger(self.inner.id);
|
||||||
previous
|
previous
|
||||||
}
|
}
|
||||||
@@ -58,6 +77,12 @@ impl<T: 'static> Cell<T> {
|
|||||||
let mut value = self.inner.value.borrow_mut();
|
let mut value = self.inner.value.borrow_mut();
|
||||||
f(&mut value)
|
f(&mut value)
|
||||||
};
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::CELL,
|
||||||
|
event = "update_cell",
|
||||||
|
node_id = self.inner.id.0,
|
||||||
|
"updated cell value in place"
|
||||||
|
);
|
||||||
self.inner.reactor.trigger(self.inner.id);
|
self.inner.reactor.trigger(self.inner.id);
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
@@ -78,11 +103,26 @@ impl<T: PartialEq + 'static> Cell<T> {
|
|||||||
pub fn set(&self, value: T) -> Option<T> {
|
pub fn set(&self, value: T) -> Option<T> {
|
||||||
let mut current = self.inner.value.borrow_mut();
|
let mut current = self.inner.value.borrow_mut();
|
||||||
if *current == value {
|
if *current == value {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::CELL,
|
||||||
|
event = "set_cell",
|
||||||
|
node_id = self.inner.id.0,
|
||||||
|
changed = false,
|
||||||
|
"suppressed unchanged cell write"
|
||||||
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let previous = std::mem::replace(&mut *current, value);
|
let previous = std::mem::replace(&mut *current, value);
|
||||||
drop(current);
|
drop(current);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::CELL,
|
||||||
|
event = "set_cell",
|
||||||
|
node_id = self.inner.id.0,
|
||||||
|
changed = true,
|
||||||
|
"set cell value"
|
||||||
|
);
|
||||||
self.inner.reactor.trigger(self.inner.id);
|
self.inner.reactor.trigger(self.inner.id);
|
||||||
Some(previous)
|
Some(previous)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::cell::{Cell, RefCell};
|
|||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
use crate::reactor::ObserverHook;
|
use crate::reactor::ObserverHook;
|
||||||
use crate::{NodeId, Reactor, current};
|
use crate::{NodeId, Reactor, current, trace_targets};
|
||||||
|
|
||||||
/// Creates an effect in the current thread's default reactor.
|
/// Creates an effect in the current thread's default reactor.
|
||||||
pub fn effect(f: impl Fn() + 'static) -> EffectHandle {
|
pub fn effect(f: impl Fn() + 'static) -> EffectHandle {
|
||||||
@@ -42,6 +42,12 @@ impl EffectHandle {
|
|||||||
self_ref: RefCell::new(Weak::new()),
|
self_ref: RefCell::new(Weak::new()),
|
||||||
});
|
});
|
||||||
*inner.self_ref.borrow_mut() = Rc::downgrade(&inner);
|
*inner.self_ref.borrow_mut() = Rc::downgrade(&inner);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::EFFECT,
|
||||||
|
event = "create_effect",
|
||||||
|
node_id = id.0,
|
||||||
|
"created reactive effect"
|
||||||
|
);
|
||||||
|
|
||||||
let observer: Rc<dyn ObserverHook> = inner.clone();
|
let observer: Rc<dyn ObserverHook> = inner.clone();
|
||||||
reactor.register_observer(id, observer);
|
reactor.register_observer(id, observer);
|
||||||
@@ -72,9 +78,27 @@ struct EffectInner {
|
|||||||
impl EffectInner {
|
impl EffectInner {
|
||||||
fn schedule(&self) {
|
fn schedule(&self) {
|
||||||
if self.disposed.get() || self.scheduled.replace(true) {
|
if self.disposed.get() || self.scheduled.replace(true) {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::EFFECT,
|
||||||
|
event = "schedule_effect",
|
||||||
|
node_id = self.id.0,
|
||||||
|
queued = false,
|
||||||
|
disposed = self.disposed.get(),
|
||||||
|
already_scheduled = self.scheduled.get(),
|
||||||
|
"effect scheduling skipped"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::EFFECT,
|
||||||
|
event = "schedule_effect",
|
||||||
|
node_id = self.id.0,
|
||||||
|
queued = true,
|
||||||
|
"queued effect for microtask flush"
|
||||||
|
);
|
||||||
let weak = self.self_ref.borrow().clone();
|
let weak = self.self_ref.borrow().clone();
|
||||||
let reactor = self.reactor.clone();
|
let reactor = self.reactor.clone();
|
||||||
reactor.schedule(move || {
|
reactor.schedule(move || {
|
||||||
@@ -87,6 +111,12 @@ impl EffectInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner.scheduled.set(false);
|
inner.scheduled.set(false);
|
||||||
|
let _span = tracing::debug_span!(
|
||||||
|
target: trace_targets::EFFECT,
|
||||||
|
"effect.run",
|
||||||
|
node_id = inner.id.0
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
inner.reactor.run_in_context(inner.id, || (inner.effect)());
|
inner.reactor.run_in_context(inner.id, || (inner.effect)());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -96,6 +126,12 @@ impl EffectInner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::EFFECT,
|
||||||
|
event = "dispose_effect",
|
||||||
|
node_id = self.id.0,
|
||||||
|
"disposed reactive effect"
|
||||||
|
);
|
||||||
self.reactor.unregister_observer(self.id);
|
self.reactor.unregister_observer(self.id);
|
||||||
self.reactor.dispose(self.id);
|
self.reactor.dispose(self.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::cell::{Cell, RefCell};
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{NodeId, Reactor, current};
|
use crate::{NodeId, Reactor, current, trace_targets};
|
||||||
|
|
||||||
type SubscriberFn<T> = dyn Fn(&T) + 'static;
|
type SubscriberFn<T> = dyn Fn(&T) + 'static;
|
||||||
|
|
||||||
@@ -68,6 +68,14 @@ impl Reactor {
|
|||||||
let mut queued = queue.borrow_mut();
|
let mut queued = queue.borrow_mut();
|
||||||
queued.drain(..).collect::<Vec<_>>()
|
queued.drain(..).collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::EVENT,
|
||||||
|
event = "drain_event_queue",
|
||||||
|
event_id = event.inner.id.0,
|
||||||
|
drained = drained.len(),
|
||||||
|
"draining queued event values reactively"
|
||||||
|
);
|
||||||
for value in &drained {
|
for value in &drained {
|
||||||
handler(value);
|
handler(value);
|
||||||
}
|
}
|
||||||
@@ -84,6 +92,12 @@ impl Reactor {
|
|||||||
impl<T: 'static> Event<T> {
|
impl<T: 'static> Event<T> {
|
||||||
fn new(reactor: Reactor) -> Self {
|
fn new(reactor: Reactor) -> Self {
|
||||||
let id = reactor.allocate_node();
|
let id = reactor.allocate_node();
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::EVENT,
|
||||||
|
event = "create_event",
|
||||||
|
node_id = id.0,
|
||||||
|
"created reactive event"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
inner: Rc::new(EventInner {
|
inner: Rc::new(EventInner {
|
||||||
reactor,
|
reactor,
|
||||||
@@ -95,6 +109,13 @@ impl<T: 'static> Event<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn observe(&self) {
|
fn observe(&self) {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::EVENT,
|
||||||
|
event = "observe_event",
|
||||||
|
event_id = self.inner.id.0,
|
||||||
|
"observing event reactively"
|
||||||
|
);
|
||||||
self.inner.reactor.observe(self.inner.id);
|
self.inner.reactor.observe(self.inner.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +128,13 @@ impl<T: 'static> Event<T> {
|
|||||||
.values()
|
.values()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::EVENT,
|
||||||
|
event = "emit_event",
|
||||||
|
event_id = self.inner.id.0,
|
||||||
|
subscriber_count = subscribers.len(),
|
||||||
|
"emitting event value"
|
||||||
|
);
|
||||||
for subscriber in subscribers {
|
for subscriber in subscribers {
|
||||||
subscriber(&value);
|
subscriber(&value);
|
||||||
}
|
}
|
||||||
@@ -121,9 +149,25 @@ impl<T: 'static> Event<T> {
|
|||||||
.subscribers
|
.subscribers
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(id, Rc::new(handler) as Rc<SubscriberFn<T>>);
|
.insert(id, Rc::new(handler) as Rc<SubscriberFn<T>>);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::EVENT,
|
||||||
|
event = "subscribe_event",
|
||||||
|
event_id = self.inner.id.0,
|
||||||
|
subscription_id = id,
|
||||||
|
subscriber_count = self.inner.subscribers.borrow().len(),
|
||||||
|
"added event subscriber"
|
||||||
|
);
|
||||||
|
|
||||||
let inner = Rc::clone(&self.inner);
|
let inner = Rc::clone(&self.inner);
|
||||||
Subscription::new(move || {
|
Subscription::new(move || {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::EVENT,
|
||||||
|
event = "unsubscribe_event",
|
||||||
|
event_id = inner.id.0,
|
||||||
|
subscription_id = id,
|
||||||
|
"removing event subscriber"
|
||||||
|
);
|
||||||
inner.subscribers.borrow_mut().remove(&id);
|
inner.subscribers.borrow_mut().remove(&id);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -173,6 +217,12 @@ impl SubscriptionInner {
|
|||||||
if !self.active.replace(false) {
|
if !self.active.replace(false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::EVENT,
|
||||||
|
event = "unsubscribe",
|
||||||
|
"cancelling subscription"
|
||||||
|
);
|
||||||
(self.cancel)();
|
(self.cancel)();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,15 @@
|
|||||||
//! single-threaded and designed to live on a runtime-managed thread, while async work feeds it
|
//! single-threaded and designed to live on a runtime-managed thread, while async work feeds it
|
||||||
//! from the edges by updating state or emitting events.
|
//! from the edges by updating state or emitting events.
|
||||||
|
|
||||||
|
pub(crate) mod trace_targets {
|
||||||
|
pub const GRAPH: &str = "ruin_reactivity::graph";
|
||||||
|
pub const CELL: &str = "ruin_reactivity::cell";
|
||||||
|
pub const THUNK: &str = "ruin_reactivity::thunk";
|
||||||
|
pub const MEMO: &str = "ruin_reactivity::memo";
|
||||||
|
pub const EFFECT: &str = "ruin_reactivity::effect";
|
||||||
|
pub const EVENT: &str = "ruin_reactivity::event";
|
||||||
|
}
|
||||||
|
|
||||||
mod cell;
|
mod cell;
|
||||||
mod effect;
|
mod effect;
|
||||||
mod event;
|
mod event;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::rc::{Rc, Weak};
|
|||||||
|
|
||||||
use ruin_runtime::queue_microtask;
|
use ruin_runtime::queue_microtask;
|
||||||
|
|
||||||
use crate::NodeId;
|
use crate::{NodeId, trace_targets};
|
||||||
|
|
||||||
type Job = Box<dyn FnOnce() + 'static>;
|
type Job = Box<dyn FnOnce() + 'static>;
|
||||||
|
|
||||||
@@ -72,20 +72,37 @@ pub struct Reactor {
|
|||||||
impl Reactor {
|
impl Reactor {
|
||||||
/// Creates a new empty reactor.
|
/// Creates a new empty reactor.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
let reactor = Self {
|
||||||
inner: Rc::new(ReactorInner::new()),
|
inner: Rc::new(ReactorInner::new()),
|
||||||
}
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "reactor_new",
|
||||||
|
"created reactive reactor"
|
||||||
|
);
|
||||||
|
reactor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current thread's default reactor.
|
/// Returns the current thread's default reactor.
|
||||||
pub fn current() -> Self {
|
pub fn current() -> Self {
|
||||||
CURRENT_REACTOR.with(|slot| {
|
CURRENT_REACTOR.with(|slot| {
|
||||||
if let Some(inner) = slot.borrow().upgrade() {
|
if let Some(inner) = slot.borrow().upgrade() {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "current_reactor_reuse",
|
||||||
|
"reusing current thread default reactor"
|
||||||
|
);
|
||||||
return Self { inner };
|
return Self { inner };
|
||||||
}
|
}
|
||||||
|
|
||||||
let reactor = Self::new();
|
let reactor = Self::new();
|
||||||
*slot.borrow_mut() = Rc::downgrade(&reactor.inner);
|
*slot.borrow_mut() = Rc::downgrade(&reactor.inner);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "current_reactor_install",
|
||||||
|
"installed current thread default reactor"
|
||||||
|
);
|
||||||
reactor
|
reactor
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -96,6 +113,12 @@ impl Reactor {
|
|||||||
/// [`observe`](Self::observe) made while `f` executes will become the observer's new
|
/// [`observe`](Self::observe) made while `f` executes will become the observer's new
|
||||||
/// dependencies.
|
/// dependencies.
|
||||||
pub fn run_in_context<T>(&self, observer: NodeId, f: impl FnOnce() -> T) -> T {
|
pub fn run_in_context<T>(&self, observer: NodeId, f: impl FnOnce() -> T) -> T {
|
||||||
|
let _span = tracing::debug_span!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
"reactor.run_in_context",
|
||||||
|
observer_id = observer.0
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
self.clear_observer_dependencies(observer);
|
self.clear_observer_dependencies(observer);
|
||||||
self.inner.stack.borrow_mut().push(observer);
|
self.inner.stack.borrow_mut().push(observer);
|
||||||
let inserted = self.inner.active_computations.borrow_mut().insert(observer);
|
let inserted = self.inner.active_computations.borrow_mut().insert(observer);
|
||||||
@@ -140,6 +163,13 @@ impl Reactor {
|
|||||||
.expect("active computation should appear in observer stack");
|
.expect("active computation should appear in observer stack");
|
||||||
let mut cycle = stack[start..].to_vec();
|
let mut cycle = stack[start..].to_vec();
|
||||||
cycle.push(observable);
|
cycle.push(observable);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "cycle_detected",
|
||||||
|
observable_id = observable.0,
|
||||||
|
cycle_len = cycle.len(),
|
||||||
|
"reactive cycle detected"
|
||||||
|
);
|
||||||
panic_any(ReactCycleError::new(cycle));
|
panic_any(ReactCycleError::new(cycle));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +178,15 @@ impl Reactor {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "observe",
|
||||||
|
observer_id = observer.0,
|
||||||
|
observable_id = observable.0,
|
||||||
|
"recording reactive dependency"
|
||||||
|
);
|
||||||
|
|
||||||
self.inner
|
self.inner
|
||||||
.dependencies
|
.dependencies
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
@@ -172,6 +211,15 @@ impl Reactor {
|
|||||||
.map(|nodes| nodes.iter().copied().collect::<Vec<_>>())
|
.map(|nodes| nodes.iter().copied().collect::<Vec<_>>())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "trigger",
|
||||||
|
observable_id = observable.0,
|
||||||
|
dependent_count = dependents.len(),
|
||||||
|
"triggering reactive dependents"
|
||||||
|
);
|
||||||
|
|
||||||
for dependent in dependents {
|
for dependent in dependents {
|
||||||
let hook = self
|
let hook = self
|
||||||
.inner
|
.inner
|
||||||
@@ -190,6 +238,12 @@ impl Reactor {
|
|||||||
|
|
||||||
/// Disposes all graph bookkeeping for `node`.
|
/// Disposes all graph bookkeeping for `node`.
|
||||||
pub fn dispose(&self, node: NodeId) {
|
pub fn dispose(&self, node: NodeId) {
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "dispose_node",
|
||||||
|
node_id = node.0,
|
||||||
|
"disposing reactive node bookkeeping"
|
||||||
|
);
|
||||||
self.clear_observer_dependencies(node);
|
self.clear_observer_dependencies(node);
|
||||||
|
|
||||||
let incoming = self
|
let incoming = self
|
||||||
@@ -218,13 +272,28 @@ impl Reactor {
|
|||||||
.pending_jobs
|
.pending_jobs
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.push_back(Box::new(job));
|
.push_back(Box::new(job));
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "schedule_job",
|
||||||
|
pending_jobs = self.inner.pending_jobs.borrow().len(),
|
||||||
|
"queued reactive job for microtask flush"
|
||||||
|
);
|
||||||
self.inner.ensure_flush_scheduled();
|
self.inner.ensure_flush_scheduled();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn allocate_node(&self) -> NodeId {
|
pub(crate) fn allocate_node(&self) -> NodeId {
|
||||||
let raw = self.inner.next_node.get();
|
let raw = self.inner.next_node.get();
|
||||||
self.inner.next_node.set(raw.wrapping_add(1));
|
self.inner.next_node.set(raw.wrapping_add(1));
|
||||||
NodeId::new(raw)
|
let id = NodeId::new(raw);
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "allocate_node",
|
||||||
|
node_id = id.0,
|
||||||
|
"allocated reactive node id"
|
||||||
|
);
|
||||||
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn register_observer(&self, id: NodeId, observer: Rc<dyn ObserverHook>) {
|
pub(crate) fn register_observer(&self, id: NodeId, observer: Rc<dyn ObserverHook>) {
|
||||||
@@ -303,6 +372,13 @@ impl ReactorInner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "schedule_flush",
|
||||||
|
pending_jobs = self.pending_jobs.borrow().len(),
|
||||||
|
"scheduling reactive microtask flush"
|
||||||
|
);
|
||||||
let reactor = Rc::clone(self);
|
let reactor = Rc::clone(self);
|
||||||
queue_microtask(move || {
|
queue_microtask(move || {
|
||||||
reactor.flush_jobs();
|
reactor.flush_jobs();
|
||||||
@@ -310,11 +386,23 @@ impl ReactorInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn flush_jobs(self: Rc<Self>) {
|
fn flush_jobs(self: Rc<Self>) {
|
||||||
|
let _span = tracing::debug_span!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
"reactor.flush_jobs"
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
loop {
|
loop {
|
||||||
let job = self.pending_jobs.borrow_mut().pop_front();
|
let job = self.pending_jobs.borrow_mut().pop_front();
|
||||||
let Some(job) = job else {
|
let Some(job) = job else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::GRAPH,
|
||||||
|
event = "run_job",
|
||||||
|
remaining_jobs = self.pending_jobs.borrow().len(),
|
||||||
|
"running reactive scheduled job"
|
||||||
|
);
|
||||||
job();
|
job();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::cell::{Cell, RefCell};
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::reactor::ObserverHook;
|
use crate::reactor::ObserverHook;
|
||||||
use crate::{NodeId, Reactor, current};
|
use crate::{NodeId, Reactor, current, trace_targets};
|
||||||
|
|
||||||
type ComputeFn<T> = dyn Fn() -> T + 'static;
|
type ComputeFn<T> = dyn Fn() -> T + 'static;
|
||||||
type EqualsFn<T> = dyn Fn(&T, &T) -> bool + 'static;
|
type EqualsFn<T> = dyn Fn(&T, &T) -> bool + 'static;
|
||||||
@@ -93,11 +93,24 @@ impl<T: 'static> Thunk<T> {
|
|||||||
|
|
||||||
let observer: Rc<dyn ObserverHook> = inner.clone();
|
let observer: Rc<dyn ObserverHook> = inner.clone();
|
||||||
reactor.register_observer(id, observer);
|
reactor.register_observer(id, observer);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::THUNK,
|
||||||
|
event = "create_thunk",
|
||||||
|
node_id = id.0,
|
||||||
|
"created reactive thunk"
|
||||||
|
);
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs `f` with a shared reference to the current computed value.
|
/// Runs `f` with a shared reference to the current computed value.
|
||||||
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
|
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::THUNK,
|
||||||
|
event = "read_thunk",
|
||||||
|
node_id = self.inner.id.0,
|
||||||
|
"reading thunk value"
|
||||||
|
);
|
||||||
self.inner.reactor.observe(self.inner.id);
|
self.inner.reactor.observe(self.inner.id);
|
||||||
self.inner.ensure_value();
|
self.inner.ensure_value();
|
||||||
let value = self.inner.value.borrow();
|
let value = self.inner.value.borrow();
|
||||||
@@ -132,11 +145,24 @@ impl<T: 'static> Memo<T> {
|
|||||||
|
|
||||||
let observer: Rc<dyn ObserverHook> = inner.clone();
|
let observer: Rc<dyn ObserverHook> = inner.clone();
|
||||||
reactor.register_observer(id, observer);
|
reactor.register_observer(id, observer);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::MEMO,
|
||||||
|
event = "create_memo",
|
||||||
|
node_id = id.0,
|
||||||
|
"created reactive memo"
|
||||||
|
);
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs `f` with a shared reference to the current computed value.
|
/// Runs `f` with a shared reference to the current computed value.
|
||||||
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
|
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::MEMO,
|
||||||
|
event = "read_memo",
|
||||||
|
node_id = self.inner.id.0,
|
||||||
|
"reading memo value"
|
||||||
|
);
|
||||||
self.inner.reactor.observe(self.inner.id);
|
self.inner.reactor.observe(self.inner.id);
|
||||||
self.inner.ensure_value();
|
self.inner.ensure_value();
|
||||||
let value = self.inner.value.borrow();
|
let value = self.inner.value.borrow();
|
||||||
@@ -167,6 +193,12 @@ impl<T> ThunkInner<T> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _span = tracing::debug_span!(
|
||||||
|
target: trace_targets::THUNK,
|
||||||
|
"thunk.recompute",
|
||||||
|
node_id = self.id.0
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
let next = self.reactor.run_in_context(self.id, || (self.compute)());
|
let next = self.reactor.run_in_context(self.id, || (self.compute)());
|
||||||
*self.value.borrow_mut() = Some(next);
|
*self.value.borrow_mut() = Some(next);
|
||||||
self.dirty.set(false);
|
self.dirty.set(false);
|
||||||
@@ -191,6 +223,12 @@ impl<T> MemoInner<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn recompute(&self) -> bool {
|
fn recompute(&self) -> bool {
|
||||||
|
let _span = tracing::debug_span!(
|
||||||
|
target: trace_targets::MEMO,
|
||||||
|
"memo.recompute",
|
||||||
|
node_id = self.id.0
|
||||||
|
)
|
||||||
|
.entered();
|
||||||
let next = self.reactor.run_in_context(self.id, || (self.compute)());
|
let next = self.reactor.run_in_context(self.id, || (self.compute)());
|
||||||
let mut value = self.value.borrow_mut();
|
let mut value = self.value.borrow_mut();
|
||||||
let changed = match value.as_ref() {
|
let changed = match value.as_ref() {
|
||||||
@@ -199,13 +237,29 @@ impl<T> MemoInner<T> {
|
|||||||
};
|
};
|
||||||
*value = Some(next);
|
*value = Some(next);
|
||||||
self.dirty.set(false);
|
self.dirty.set(false);
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::MEMO,
|
||||||
|
event = "memo_recompute",
|
||||||
|
node_id = self.id.0,
|
||||||
|
changed,
|
||||||
|
"recomputed memo"
|
||||||
|
);
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: 'static> ObserverHook for ThunkInner<T> {
|
impl<T: 'static> ObserverHook for ThunkInner<T> {
|
||||||
fn notify(&self) {
|
fn notify(&self) {
|
||||||
if self.dirty.replace(true) {
|
let already_dirty = self.dirty.replace(true);
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::THUNK,
|
||||||
|
event = "invalidate_thunk",
|
||||||
|
node_id = self.id.0,
|
||||||
|
already_dirty,
|
||||||
|
"invalidating thunk"
|
||||||
|
);
|
||||||
|
if already_dirty {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,11 +279,29 @@ impl<T: 'static> ObserverHook for MemoInner<T> {
|
|||||||
fn notify(&self) {
|
fn notify(&self) {
|
||||||
if self.value.borrow().is_none() {
|
if self.value.borrow().is_none() {
|
||||||
self.dirty.set(true);
|
self.dirty.set(true);
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::MEMO,
|
||||||
|
event = "invalidate_memo",
|
||||||
|
node_id = self.id.0,
|
||||||
|
eagerly_recomputed = false,
|
||||||
|
"marked uninitialized memo dirty"
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dirty.set(true);
|
self.dirty.set(true);
|
||||||
if self.recompute() {
|
let changed = self.recompute();
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::MEMO,
|
||||||
|
event = "invalidate_memo",
|
||||||
|
node_id = self.id.0,
|
||||||
|
eagerly_recomputed = true,
|
||||||
|
changed,
|
||||||
|
"invalidated memo"
|
||||||
|
);
|
||||||
|
if changed {
|
||||||
self.reactor.trigger(self.id);
|
self.reactor.trigger(self.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ edition = "2024"
|
|||||||
hyper = { version = "1.8", default-features = false, features = ["client", "http1"] }
|
hyper = { version = "1.8", default-features = false, features = ["client", "http1"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
ruin_runtime_proc_macros = { package = "ruin-runtime-proc-macros", path = "../runtime_proc_macros" }
|
ruin_runtime_proc_macros = { package = "ruin-runtime-proc-macros", path = "../runtime_proc_macros" }
|
||||||
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ compile_error!("ruin-runtime currently supports only Linux x86_64.");
|
|||||||
|
|
||||||
extern crate alloc;
|
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 channel;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::uring::{IORING_OP_ASYNC_CANCEL, IoUring, IoUringCqe, IoUringSqe};
|
use super::uring::{IORING_OP_ASYNC_CANCEL, IoUring, IoUringCqe, IoUringSqe};
|
||||||
|
use crate::trace_targets;
|
||||||
|
|
||||||
const WAKE_TARGET_TOKEN: u64 = 1;
|
const WAKE_TARGET_TOKEN: u64 = 1;
|
||||||
const TOKEN_KIND_SHIFT: u64 = 56;
|
const TOKEN_KIND_SHIFT: u64 = 56;
|
||||||
@@ -34,6 +35,13 @@ struct NotifierInner {
|
|||||||
|
|
||||||
impl NotifierInner {
|
impl NotifierInner {
|
||||||
fn notify(&self) -> io::Result<()> {
|
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) {
|
if self.closed.load(Ordering::Acquire) {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::BrokenPipe,
|
io::ErrorKind::BrokenPipe,
|
||||||
@@ -96,6 +104,12 @@ pub fn create() -> io::Result<(Driver, ThreadNotifier)> {
|
|||||||
/// emphasize driver construction.
|
/// emphasize driver construction.
|
||||||
pub fn create_driver() -> io::Result<(Driver, ThreadNotifier)> {
|
pub fn create_driver() -> io::Result<(Driver, ThreadNotifier)> {
|
||||||
let ring = IoUring::new(64)?;
|
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 {
|
let notifier = Arc::new(NotifierInner {
|
||||||
ring_fd: ring.ring_fd(),
|
ring_fd: ring.ring_fd(),
|
||||||
closed: AtomicBool::new(false),
|
closed: AtomicBool::new(false),
|
||||||
@@ -130,11 +144,27 @@ impl Driver {
|
|||||||
let saw_any = self
|
let saw_any = self
|
||||||
.ring
|
.ring
|
||||||
.drain_completions(|cqe| self.process_cqe(cqe, &mut ready));
|
.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) }
|
if saw_any { Ok(Some(ready)) } else { Ok(None) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocks until at least one completion is available.
|
/// Blocks until at least one completion is available.
|
||||||
pub fn wait(&self) -> io::Result<()> {
|
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()
|
self.ring.wait_for_cqe()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +172,13 @@ impl Driver {
|
|||||||
///
|
///
|
||||||
/// Passing `None` removes any active timer.
|
/// Passing `None` removes any active timer.
|
||||||
pub fn rearm_timer(&self, deadline: Option<Duration>) -> io::Result<()> {
|
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) {
|
match (self.active_timer_token.get(), deadline) {
|
||||||
(Some(active), Some(deadline)) => {
|
(Some(active), Some(deadline)) => {
|
||||||
self.ring.submit_timeout_update(active, deadline)?;
|
self.ring.submit_timeout_update(active, deadline)?;
|
||||||
@@ -168,6 +205,13 @@ impl Driver {
|
|||||||
on_complete: impl FnOnce(IoUringCqe) + Send + 'static,
|
on_complete: impl FnOnce(IoUringCqe) + Send + 'static,
|
||||||
) -> io::Result<u64> {
|
) -> io::Result<u64> {
|
||||||
let token = self.next_token(CompletionKind::Operation);
|
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
|
self.completions
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.insert(token, Box::new(on_complete));
|
.insert(token, Box::new(on_complete));
|
||||||
@@ -181,6 +225,13 @@ impl Driver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cancel_operation(&self, token: u64) -> io::Result<()> {
|
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
|
self.ring
|
||||||
.submit_with_token(self.next_token(CompletionKind::OperationCancel), |sqe| {
|
.submit_with_token(self.next_token(CompletionKind::OperationCancel), |sqe| {
|
||||||
sqe.opcode = IORING_OP_ASYNC_CANCEL;
|
sqe.opcode = IORING_OP_ASYNC_CANCEL;
|
||||||
@@ -216,6 +267,14 @@ impl Driver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn process_cqe(&self, cqe: IoUringCqe, ready: &mut ReadyEvents) {
|
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 {
|
if cqe.user_data == WAKE_TARGET_TOKEN {
|
||||||
ready.wake = true;
|
ready.wake = true;
|
||||||
let wakes = cqe.res.max(1) as u64;
|
let wakes = cqe.res.max(1) as u64;
|
||||||
@@ -256,6 +315,11 @@ impl Driver {
|
|||||||
|
|
||||||
impl Drop for Driver {
|
impl Drop for Driver {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::DRIVER,
|
||||||
|
event = "drop_driver",
|
||||||
|
"dropping runtime driver"
|
||||||
|
);
|
||||||
self.notifier.closed.store(true, Ordering::Release);
|
self.notifier.closed.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::driver::{Driver, ThreadNotifier, create_driver, monotonic_now};
|
use super::driver::{Driver, ThreadNotifier, create_driver, monotonic_now};
|
||||||
|
use crate::trace_targets;
|
||||||
|
|
||||||
type LocalTask = Box<dyn FnOnce() + 'static>;
|
type LocalTask = Box<dyn FnOnce() + 'static>;
|
||||||
type SendTask = Box<dyn FnOnce() + Send + 'static>;
|
type SendTask = Box<dyn FnOnce() + Send + 'static>;
|
||||||
@@ -95,6 +96,13 @@ pub fn queue_task<F>(task: F)
|
|||||||
where
|
where
|
||||||
F: FnOnce() + 'static,
|
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));
|
push_local_macrotask(Box::new(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +118,13 @@ pub fn queue_microtask<F>(task: F)
|
|||||||
where
|
where
|
||||||
F: FnOnce() + 'static,
|
F: FnOnce() + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::SCHEDULER,
|
||||||
|
event = "queue_microtask",
|
||||||
|
queue = "local_micro",
|
||||||
|
"queueing local microtask"
|
||||||
|
);
|
||||||
current_thread()
|
current_thread()
|
||||||
.local_microtasks
|
.local_microtasks
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
@@ -128,6 +143,15 @@ where
|
|||||||
let owner = current_thread_ptr();
|
let owner = current_thread_ptr();
|
||||||
let id = allocate_timer_id();
|
let id = allocate_timer_id();
|
||||||
let deadline = deadline_from_now(delay);
|
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));
|
let timer = TimerNode::timeout(id, deadline, Box::new(callback));
|
||||||
|
|
||||||
current_thread().timers.borrow_mut().insert(timer);
|
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`.
|
/// Panics if called from a different runtime thread than the one that created `handle`.
|
||||||
pub fn clear_timeout(handle: &TimeoutHandle) {
|
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);
|
clear_timer(handle.owner, handle.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +194,15 @@ where
|
|||||||
let owner = current_thread_ptr();
|
let owner = current_thread_ptr();
|
||||||
let id = allocate_timer_id();
|
let id = allocate_timer_id();
|
||||||
let deadline = deadline_from_now(delay);
|
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(
|
let timer = TimerNode::interval(
|
||||||
id,
|
id,
|
||||||
deadline,
|
deadline,
|
||||||
@@ -186,6 +226,13 @@ where
|
|||||||
///
|
///
|
||||||
/// Panics if called from a different runtime thread than the one that created `handle`.
|
/// Panics if called from a different runtime thread than the one that created `handle`.
|
||||||
pub fn clear_interval(handle: &IntervalHandle) {
|
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);
|
clear_timer(handle.owner, handle.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +259,12 @@ where
|
|||||||
F: Future + 'static,
|
F: Future + 'static,
|
||||||
F::Output: '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 state = Rc::new(JoinState::new());
|
||||||
let completion = Rc::clone(&state);
|
let completion = Rc::clone(&state);
|
||||||
let task = Rc::new(FutureTask {
|
let task = Rc::new(FutureTask {
|
||||||
@@ -240,6 +293,11 @@ where
|
|||||||
Init: FnOnce() + Send + 'static,
|
Init: FnOnce() + Send + 'static,
|
||||||
Exit: FnOnce() + 'static,
|
Exit: FnOnce() + 'static,
|
||||||
{
|
{
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::RUNTIME,
|
||||||
|
event = "spawn_worker",
|
||||||
|
"spawning runtime worker thread"
|
||||||
|
);
|
||||||
let parent = current_thread();
|
let parent = current_thread();
|
||||||
let (driver, notifier) = create_driver().expect("worker driver should initialize");
|
let (driver, notifier) = create_driver().expect("worker driver should initialize");
|
||||||
let shared = Arc::new(ThreadShared::new(notifier));
|
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.
|
/// Panics if runtime initialization fails or if the underlying driver returns an unexpected error.
|
||||||
pub fn run() {
|
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();
|
let _ = current_thread();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -316,6 +384,15 @@ pub fn run() {
|
|||||||
|
|
||||||
if has_pending_timers() || state.has_live_children() || state.has_live_async_operations() {
|
if has_pending_timers() || state.has_live_children() || state.has_live_async_operations() {
|
||||||
state.shared.closing.store(false, Ordering::Release);
|
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");
|
state.driver.wait().expect("driver wait should succeed");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -327,6 +404,11 @@ pub fn run() {
|
|||||||
|
|
||||||
state.shared.closed.store(true, Ordering::Release);
|
state.shared.closed.store(true, Ordering::Release);
|
||||||
state.shared.notify();
|
state.shared.notify();
|
||||||
|
tracing::debug!(
|
||||||
|
target: trace_targets::RUNTIME,
|
||||||
|
event = "run_exit",
|
||||||
|
"runtime event loop exiting"
|
||||||
|
);
|
||||||
teardown_thread();
|
teardown_thread();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -351,7 +433,16 @@ impl ThreadHandle {
|
|||||||
where
|
where
|
||||||
F: FnOnce() + Send + 'static,
|
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.
|
/// Queues a microtask onto this runtime thread.
|
||||||
@@ -361,7 +452,16 @@ impl ThreadHandle {
|
|||||||
where
|
where
|
||||||
F: FnOnce() + Send + 'static,
|
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.
|
/// Returns `true` if the target runtime thread has shut down.
|
||||||
@@ -859,12 +959,24 @@ fn drain_driver_events() {
|
|||||||
|
|
||||||
let state = current_thread();
|
let state = current_thread();
|
||||||
if ready.wake {
|
if ready.wake {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::DRIVER,
|
||||||
|
event = "drain_wake",
|
||||||
|
"draining driver wake notifications"
|
||||||
|
);
|
||||||
let _ = state
|
let _ = state
|
||||||
.driver
|
.driver
|
||||||
.drain_wake()
|
.drain_wake()
|
||||||
.expect("wake drain should succeed");
|
.expect("wake drain should succeed");
|
||||||
}
|
}
|
||||||
if ready.timer {
|
if ready.timer {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
tracing::trace!(
|
||||||
|
target: trace_targets::TIMER,
|
||||||
|
event = "drain_timer",
|
||||||
|
"draining expired runtime timers"
|
||||||
|
);
|
||||||
let _ = state
|
let _ = state
|
||||||
.driver
|
.driver
|
||||||
.drain_timer()
|
.drain_timer()
|
||||||
|
|||||||
Reference in New Issue
Block a user