runtime-rs
Tokio-native lifecycle and service composition for Rust applications.
runtime-rs gives your app one place to register services, boot them,
reload them, run their background tasks, resolve them by Rust type, and shut
them down gracefully:
- register typed services once
- boot / validate / reload / shutdown them in deterministic order
- resolve them later by concrete Rust type
- remove the need for ad-hoc runtime injection
- spawn long-running runnable providers
- drain accepted work through graceful shutdown gates
- publish typed in-process lifecycle events with the
eventsfeature
The core idea:
Registry<State>
├─ DbService -> Provider + Reloadable
├─ CacheService -> Provider + Runnable
└─ YourService -> Provider
Runtime<State>
└─ spawns every provider that exposes Runnable
Features
Default features are:
= ["registry"]
= ["registry", "support", "events"]
registryaddsregistry_ref()toSharedStatefor states that carryRegistry<Self>. TheRegistrytype itself is always available.eventsenablesLifecycleBus, addsevents()toSharedState, and enables thedashmapdependency.supportenablesGate,Permit,GuardGroup, andGuard.
Your State
The crate does not own your app state. Your state only needs to implement
SharedState. If you want registry.reload_all(&state).await, implement
ReloadState too.
use async_trait;
use Arc;
use CancellationToken;
use LifecycleBus;
use ;
;
The Registry
Registry<S> is the main piece. It stores Arc<T> values by TypeId, while
also treating them as lifecycle providers.
That means one object can be all of these at once:
- a concrete service you can resolve later:
registry.resolve::<DbService>() - a lifecycle participant:
boot,validate,shutdown - a hot-reload participant:
Reloadable - a long-running background task:
Runnable
use Arc;
let state = new;
state
.registry_ref
.insert
.insert;
state.registry_ref.boot_all.await?;
state.registry_ref.validate_all?;
let db = state.registry_ref..expect;
Register once. Boot, reload, run, and resolve by type.
Providers
A provider is any service that wants to join the application lifecycle.
use async_trait;
use ;
;
Lifecycle order is deterministic. Providers can override boot_priority() and
run_priority() when ordering matters.
Runnable Providers
Long-running loops live in Runnable::run(), not in boot().
use ;
Then the runtime starts every runnable provider:
use ;
let state = new;
let mut runtime = default;
runtime.spawn_all;
state.initiate_shutdown;
runtime.wait_until_shutdown.await?;
runtime.drain.await?;
# Ok::
Reloadable Providers
Reload is a capability, not a second registry.
use async_trait;
use ;
Use registry.reload_one("db", &state).await for targeted reloads or
registry.reload_all(&state).await for full reloads.
Gates
The support feature enables optional runtime support tools. They are not
required by Registry or Runtime, but they are useful when implementing
servers, connection loops, and request handlers.
Gate is a graceful shutdown admission/drain tool. It is inspired by axum's
server handle pattern, but lifted out of HTTP.
It can:
- reject new work after graceful shutdown starts
- track accepted in-flight work with
Permit - wait until all permits drop
- force shutdown after a grace period
- optionally apply simple max-in-flight admission control
use Duration;
use Gate;
let gate = new;
let permit = gate.enter.await?;
gate.graceful_shutdown;
gate.wait_all_done.await;
# Ok::
GuardGroup is a smaller RAII in-flight counter for places where you only
need “count active work and wait until zero” without admission control.
Lifecycle Events
The events feature enables LifecycleBus, a typed process-local event bus.
Use it when services need a loose in-process signal without depending on each
other directly.
use LifecycleBus;
;
let bus = new;
let mut rx = bus.;
bus.emit;
Examples
The library crate keeps small single-file examples:
examples/minimal.rs
examples/axum.rs
Run them with:
axum is a dev-dependency only. It is there to show integration style; it is
not part of runtime-rs for library users.
Dependencies
The dependency set is intentionally small and canonical for Tokio-based services:
= "0.1"
= "6"
= { = "1", = ["macros", "rt", "sync", "time"] }
= "0.7"
= "0.1"
dashmap is only required by the default events feature.
License
Licensed under either of:
- MIT license (LICENSE-MIT)
- Apache License, Version 2.0 (LICENSE-APACHE)