Ephemeral, cancellable task rooms with scoped resources for Tokio. Check a workflow in, give it its own scratchpad and budgets, and guarantee a clean checkout with cancellation and hooks.
use tokio_hotel::{Hotel, RoomConfig, bytes};
use std::time::{Duration, Instant};
#[tokio::main]
async fn main() {
let hotel = Hotel::new();
let room = hotel.check_in(RoomConfig {
wall_timeout: Some(Duration::from_millis(5)), // cooperative wall-clock cancel
mem_budget: Some(bytes!(512.kb)), // enforced for scratchpad
io_limits: None,
deadline: Some(Instant::now() + Duration::from_secs(2)),
name: Some("ingest".into()),
});
let report = room.run(|ctx| async move -> Result<&'static str, RoomError> {
ctx.scratch.put("hello", b"world").await?;
ctx.typed_scratch::<u32>().put("answer", &42).await?;
ctx.yield_now().await; // cooperative yield to honor cancellation
Ok("done")
}).await;
println!("room {:?} -> {:?}", report.summary.outcome, report.value);
}Every Tokio shop eventually builds a scratch “room” for short-lived workflows: spawn a task, give it per-task limits, a temporary state bag, trace it, then kill everything inside on timeout or cancellation. This crate makes that pattern first-class.
Problems it tackles
- Structured deadlines that actually propagate
- Per-workflow resource groups (scratchpad today; pluggable helpers later)
- Cancellation that hits children via
CancellationToken - Predictable teardown and user-provided checkout hooks
- Tracing spans per room for replay-friendly logs
Hotelhands out room IDs and holds global checkout hooks.RoomConfigdeclares enforced budgets (wall-clock timeout, scratchpad memory), IO hints, and an enforced deadline.Room::runexecutes a workflow inside a tracing span, respecting deadlines and cancellation.RoomContextprovides a cancellation token, a scratchpad, and a span; child tasks can be spawned viactx.spawn.- Dropping a
Roomcancels everything inside.
Current: minimal, safe, and composable foundation—deadline + cancellation + scratchpad (with budget) + hooks + nested rooms.
Next directions:
- Enforceable CPU/memory/IO budgets (cgroups/limiter integrations)
- Rate-limited HTTP client helper and per-room metrics collector
- Nested rooms (“suites”) for tree-structured agent flows
- Structured logging out of the box (
tracingevents per checkout)
- Deadlines are enforced with
tokio::time::timeout_at; on expiry all work in the room is cancelled. - Wall-clock budget is enforced as a timeout from check-in; whichever arrives first (deadline vs wall budget) wins and surfaces in
RoomOutcomeKind. - Cooperative cancellation: budgets rely on async code yielding; a tight busy-loop can overrun. Design for well-behaved async tasks or add your own yields/
tokio::task::yield_now(). - Dropping a
Roomhandle cancels everything inside immediately. - Report awaits vs. background:
room.run(...).awaitdrives the room to completion; if you drop theRoombefore awaiting theRoomReport, cancellation still happens. There’s no background supervisor; cancellation is wired through theCancellationTokenheld by the room/children. - Scratchpad lifetime: in-room scratch data is in-memory,
Send + Syncvia an internalMutex<HashMap<String, Vec<u8>>>, budgeted bymem_budget, and wiped at checkout. You get both raw bytes and a typed wrapper (ctx.typed_scratch<T>()) usingserde+bincode. - Hotel lifecycle:
Hotelis cheap toClone,Send + Sync, and safe to share (e.g., inside anArc). Dropping aHoteldoes not cancel existing rooms; rooms own their own tokens.
MIT.