feat: try_tick() for single-threaded runtimes
Make single-threaded tick driving observable so callers can tell whether a tick actually performed work. - src/runtime.rs: add try_tick() returning whether any worker did work, add has_work() reporting schedulable work, and reduce tick() to a thin wrapper that ignores try_tick()'s result - src/worker.rs: extract the fast-idle predicate into a reusable Worker::has_work() (backlog plus non-empty spawn/transfer/admin queues plus pending extension work) and reuse it from both the new runtime has_work() and the idle short-circuit in tick_once() Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
This commit is contained in:
parent
9f950aa985
commit
a185c8068c
2 changed files with 44 additions and 17 deletions
|
|
@ -438,18 +438,42 @@ impl Runtime {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return whether the single-threaded runtime currently has schedulable work.
|
||||
///
|
||||
/// Panics if called on a multi-threaded runtime — use `run()` instead.
|
||||
pub fn has_work(&self) -> bool {
|
||||
assert!(
|
||||
self.config.num_threads < 2,
|
||||
"has_work() is only valid for single-threaded runtimes; use run() for multi-threaded"
|
||||
);
|
||||
|
||||
self.tick_workers.borrow().iter().any(Worker::has_work)
|
||||
}
|
||||
|
||||
/// Try to drive one tick of the single-threaded runtime.
|
||||
///
|
||||
/// Returns `false` if no worker performed work.
|
||||
/// Returns `true` if at least one worker performed work.
|
||||
///
|
||||
/// Panics if called on a multi-threaded runtime — use `run()` instead.
|
||||
pub fn try_tick(&self) -> bool {
|
||||
assert!(
|
||||
self.config.num_threads < 2,
|
||||
"try_tick() is only valid for single-threaded runtimes; use run() for multi-threaded"
|
||||
);
|
||||
|
||||
let tc = self.make_tick_context();
|
||||
self.tick_workers
|
||||
.borrow_mut()
|
||||
.iter_mut()
|
||||
.fold(false, |did_work, worker| worker.tick_once(&tc) || did_work)
|
||||
}
|
||||
|
||||
/// Drive one tick of the single-threaded worker.
|
||||
///
|
||||
/// Panics if called on a multi-threaded runtime — use `run()` instead.
|
||||
pub fn tick(&self) {
|
||||
assert!(
|
||||
self.config.num_threads < 2,
|
||||
"tick() is only valid for single-threaded runtimes; use run() for multi-threaded"
|
||||
);
|
||||
let tc = self.make_tick_context();
|
||||
for worker in self.tick_workers.borrow_mut().iter_mut() {
|
||||
worker.tick_once(&tc);
|
||||
}
|
||||
let _ = self.try_tick();
|
||||
}
|
||||
|
||||
/// Spawn worker threads and start processing, returning a handle
|
||||
|
|
|
|||
|
|
@ -105,6 +105,17 @@ impl Worker {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn has_work(&self) -> bool {
|
||||
self.has_backlog
|
||||
|| !self.spawn_rx.is_empty()
|
||||
|| !self.transfer_rx.is_empty()
|
||||
|| !self.admin_rx.is_empty()
|
||||
|| self
|
||||
.worker_ext
|
||||
.as_ref()
|
||||
.map_or(false, |e| e.has_pending_work())
|
||||
}
|
||||
|
||||
/// Run one iteration of the worker loop. Returns `true` if any work was done.
|
||||
/// Drain the spawn queue, inserting new actors into the pool.
|
||||
/// Used in phases 1 and 4 of tick_once.
|
||||
|
|
@ -283,15 +294,7 @@ impl Worker {
|
|||
|
||||
// Fast idle path: skip the entire tick when nothing could have changed.
|
||||
// Cost: ~3 atomic loads, zero syscalls, zero actor iteration.
|
||||
if !self.has_backlog
|
||||
&& self.spawn_rx.is_empty()
|
||||
&& self.transfer_rx.is_empty()
|
||||
&& self.admin_rx.is_empty()
|
||||
&& !self
|
||||
.worker_ext
|
||||
.as_ref()
|
||||
.map_or(false, |e| e.has_pending_work())
|
||||
{
|
||||
if !self.has_work() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue