pub struct Runtime { /* private fields */ }Expand description
Single-threaded async runtime.
Runtime is intrinsically thread-bound — its slab TLS state is
per-thread, so moving it to another thread would silently
desynchronize allocation dispatch. The type is therefore both
!Send and !Sync, enforced by a PhantomData<*const ()> marker.
use nexus_async_rt::Runtime;
fn assert_send<T: Send>() {}
assert_send::<Runtime>();use nexus_async_rt::Runtime;
fn assert_sync<T: Sync>() {}
assert_sync::<Runtime>();§Examples
use nexus_async_rt::{Runtime, spawn_boxed, spawn_slab};
use nexus_slab::byte::unbounded::Slab;
use nexus_rt::WorldBuilder;
let mut world = WorldBuilder::new().build();
// Simple — Box-allocated tasks
let mut rt = Runtime::new(&mut world);
rt.block_on(async {
spawn_boxed(async { /* Box-allocated */ });
});
// With slab for hot-path tasks
let slab = unsafe { Slab::<256>::with_chunk_capacity(64) };
let mut rt = Runtime::builder(&mut world)
.slab_unbounded(slab)
.build();
rt.block_on(async {
spawn_boxed(async { /* Box-allocated */ });
spawn_slab(async { /* slab-allocated */ });
});Implementations§
Source§impl Runtime
impl Runtime
Sourcepub fn new(world: &mut World) -> Self
pub fn new(world: &mut World) -> Self
Create a runtime with default settings. Box-allocated tasks only.
For slab allocation or custom configuration, use Runtime::builder.
Sourcepub fn builder(world: &mut World) -> RuntimeBuilder<'_>
pub fn builder(world: &mut World) -> RuntimeBuilder<'_>
Create a runtime via the builder pattern.
Sourcepub fn shutdown_handle(&self) -> ShutdownHandle
pub fn shutdown_handle(&self) -> ShutdownHandle
Returns a ShutdownHandle for triggering or observing shutdown.
Sourcepub fn install_signal_handlers(&self)
pub fn install_signal_handlers(&self)
Install signal handlers for SIGTERM and SIGINT.
Sourcepub fn task_count(&self) -> usize
pub fn task_count(&self) -> usize
Number of live spawned tasks.
Sourcepub fn shutdown_stats(&self) -> Arc<ShutdownStatsAtomics>
pub fn shutdown_stats(&self) -> Arc<ShutdownStatsAtomics>
Returns a handle to the abnormal-shutdown counter atomics.
Hold the handle past drop(runtime) to inspect final
values — the counters fire DURING Executor::drop, so a
snapshot taken before drop will show all zeros for the
shutdown-only counters.
Useful as a signal — if any counter is non-zero, the shutdown hit a defensive path that should be investigated.
let stats_handle = runtime.shutdown_stats();
drop(runtime);
let stats = stats_handle.snapshot();
if stats.aborted_unwinds != 0
|| stats.leaked_box_tasks != 0
|| stats.unbalanced_normal_shutdowns != 0
|| stats.cross_queue_undrained != 0
{
// user's own observability — log to wherever they want
my_logger::warn!("nexus runtime shutdown: {stats:?}");
}Per PR 2’s design (CALLOUT 5 of the plan), the runtime emits no
log events of its own when these counters fire — users own
their observability stack. The PR 1a eprintln! calls in the
slab-unwinding-abort path remain (the only signal at the
moment of process abort) but new abnormal paths added in PR 2
are pure counter increments.
§Counters
See ShutdownStats for what each
counter signifies, and [ShutdownStatsAtomics::snapshot] for
the read API on the returned handle.
Source§impl Runtime
impl Runtime
Sourcepub fn block_on<F>(&mut self, future: F) -> F::Outputwhere
F: Future + 'static,
pub fn block_on<F>(&mut self, future: F) -> F::Outputwhere
F: Future + 'static,
Drive the root future to completion. CPU-friendly.
Parks the thread when no work is available.
Sourcepub fn block_on_busy<F>(&mut self, future: F) -> F::Outputwhere
F: Future + 'static,
pub fn block_on_busy<F>(&mut self, future: F) -> F::Outputwhere
F: Future + 'static,
Drive the root future to completion. Busy-wait.
Never parks. Minimum wake latency at 100% CPU.
Sourcepub fn shutdown_quiesce(
&mut self,
timeout: Duration,
) -> Result<(), QuiesceTimeout>
pub fn shutdown_quiesce( &mut self, timeout: Duration, ) -> Result<(), QuiesceTimeout>
Drive the executor until pending cross-thread work has settled,
before shutdown. The canonical “quiesce” step before
drop(runtime) — see docs/SHUTDOWN.md for the full pattern.
Loops while:
- The cross-thread queue has entries (drains them).
- The local ready queue has entries (polls them).
Returns Ok(()) once both are empty (or detected to no longer
receive new entries). Returns Err(QuiesceTimeout) if timeout
elapses first; the error contains diagnostic counts useful for
determining which producer didn’t release its refs.
This is for clean shutdown, not panic-during-shutdown. The
100ms unwinding-wait in Executor::drop remains as
defense-in-depth for the panic case (where this method can’t
be called).
The timeout parameter has no default — callers must pick a
budget deliberately. PR 2 §2.4 open-item 4 evaluated 100ms
(matches the unwinding defense), 500ms (forgiving), and
“parameter-only” — chose parameter-only to force the user to
pick a budget appropriate for their producer landscape (a
trading-system shutdown sequence with multiple Aeron drivers
plus tokio futures plus channel senders has very different
settling characteristics than a unit test).
§Canonical shutdown sequence
// 1. Stop producers of cross-thread refs:
// - Drop tokio runtime (or shutdown_timeout)
// - Stop Aeron driver thread
// - Drop external channel senders
// 2. Quiesce.
runtime.shutdown_quiesce(Duration::from_millis(500))?;
// 3. Drop the Runtime. Outstanding-ref panic paths in
// Executor::drop should be unreachable in normal operation.
drop(runtime);If step 2 returns QuiesceTimeout, a producer hasn’t released
its refs. Investigate before letting Runtime drop — the
unwind-abort path in Executor::drop is defensive, not
desired.