Four short programs take you from a plain #[main] to a lab test that catches a leaked permit. Each one is a real file in the repository's examples/ directory.
v0.5.0 is the latest release on crates.io. The main branch carries the unreleased 0.6.0 line.
cargo add asupersynccargo add asupersync --git https://github.com/Dicklesworthstone/asupersync? on Outcome; on stable it's inactive and the crate builds without it. The repository pins nightly-2026-08-31 for its own tests.#[main], join!, race!, and friends) and the lab runtime come with the default build. TLS, HTTP/3, databases, and io_uring are opt-in features, listed below.Each level adds one layer. Run any of them from a checkout with cargo run --example onramp_level0 (through 3).
Enter the runtime
The attribute entry point. No builder, executor handle, or capability to learn yet.
examples/onramp_level0.rs01 asupersync::main;02 03#[main]04 main() {05 println!("hello from asupersync");06}Cx, Outcome, and Budget
main receives the capability context. Budgets combine with meet, and the tighter one wins.
examples/onramp_level1.rs01 asupersync::{main, prelude::*};02 03#[main]04 main(cx: &Cx) {05 service = Budget::new().with_poll_quota(64);06 request = Budget::new().with_poll_quota(16);07 effective = service.meet(request);08 assert_eq!(effective.remaining_polls(), 16);09 10 cx.checkpoint()11 .expect("budget and cancellation permit work");12 outcome: Outcome<u32, Error> = Outcome::ok(42);13 assert_eq!(outcome.expect("work succeeds"), 42);14}Scopes and region-owned fan-out
A JoinSet owns dynamic fan-out inside one region; nothing outlives the join.
examples/onramp_level2.rs01 asupersync::{main, prelude::*};02 03#[main]04 main(cx: &Cx) {05 scope = cx.scope_with_budget(Budget::new().with_poll_quota(64));06 tasks = JoinSet::new(&scope);07 08 value 1..=3_u32 {09 tasks10 .spawn(cx, |_| { Ok::<_, Error>(value) })11 .expect("spawn region-owned task");12 }13 14 sum = tasks15 .join_all(cx)16 .17 .into_iter()18 .map(|outcome| outcome.expect("child succeeds"))19 .sum::<u32>();20 assert_eq!(sum, 6);21}Two-phase sends and a lab oracle
Reserve, then send. Then leak a permit on purpose inside the lab and watch the obligation-leak oracle name it.
examples/onramp_level3.rs01 asupersync::{LabConfig, LabRuntime, main, prelude::*};02 03#[main]04 main(cx: &Cx) {05 (tx, rx) = mpsc::channel::<u8>(1);06 permit = tx.reserve(cx)..expect("reserve channel capacity");07 permit.send(7);08 assert_eq!(rx.recv(cx)..expect("receive committed value"), 7);09 10 // The same reservation inside a deterministic lab task, except that this11 // permit escapes without `send` or `abort`. Stock permits are runtime12 // obligations, so the lab's obligation-leak oracle catches it by kind13 // without any hand-built obligation record.14 lab = LabRuntime::new(LabConfig::new(7).panic_on_leak(false));15 region = lab.state.create_root_region(Budget::INFINITE);16 (task, _handle) = lab17 .state18 .create_task(region, Budget::INFINITE, {19 cx = Cx::current().expect("lab task installs a current Cx");20 (tx, _rx) = mpsc::channel::<u8>(1);21 permit = tx.reserve(&cx)..expect("reserve channel capacity");22 std::mem::forget(permit); // deliberate on-ramp leak23 })24 .expect("create lab task");25 lab.scheduler.lock().schedule(task, 0);26 lab.run_until_quiescent();27 28 report = lab.report();29 leak = report30 .oracle_report31 .entry("obligation_leak")32 .expect("obligation leak oracle is registered");33 assert!(!leak.passed, "the lab must catch the deliberate leak");34 violation = leak.violation.as_deref().unwrap_or_default();35 assert!(36 violation.contains("SendPermit"),37 "the oracle names the leaked permit kind: {violation}"38 );39}The full walkthrough, with what each level's output means, is the on-ramp guide in the spec docs.
Fibers for cheap fan-out over borrowed data, and the lab runtime proving a run replays.
fiber::scope runs its fibers inside the calling task, so they can borrow from the stack and need no 'static bound. The scope returns only after every fiber finishes.
01 asupersync::cx::fiber;02 asupersync::main;03 04#[main]05 main() {06 words = vec!["structured", "concurrency", "over", "borrowed", "data"];07 words = &words;08 letters = fiber::scope(|scope| {09 handles: Vec<_> = words10 .iter()11 .map(|word| scope.spawn( { word.len() }))12 .collect();13 letters = 0;14 handle handles {15 letters += handle..expect("fiber finished");16 }17 letters18 })19 .;20 assert_eq!(letters, words.iter().map(|word| word.len()).sum::<usize>());21 println!("{letters} letters counted by {} fibers", words.len());22}The same seed replays the same execution, and the report says whether the run reached quiescence and whether any invariant was violated.
01 asupersync::lab::{LabRunReport, run_async_under_lab};02 asupersync::prelude::*;03 04 main() {05 (total, report) = fanout(7);06 assert_eq!(total, fanout(7).0, "same seed must replay identically");07 assert!(report.quiescent, "region close implies quiescence");08 assert!(report.invariant_violations.is_empty(), "nothing leaked");09 println!("deterministic: replayed {total} under seed 7, quiescent, 0 violations");10}11 12/// Sums a fan-out of 8 children under the deterministic lab runtime.13 fanout(seed: u64) -> (u32, LabRunReport) {14 run_async_under_lab(seed, |cx| {15 set = JoinSet::in_cx(&cx);16 i 0..8_u32 {17 set.spawn(&cx, |_| { Ok::<_, ()>(i) })18 .expect("spawn");19 }20 outcomes = set.join_all(&cx).;21 outcomes.into_iter().map(|o| o.expect("member ok")).sum()22 })23}Most early surprises come from tokio habits that are harmless there and break a guarantee here.
Cancellation is cooperative. A task that never reaches a cancellation point is never stopped, and its region can't close.
The lab runtime runs on virtual time. Reading the wall clock makes a seed stop reproducing its schedule.
Ambient entropy breaks determinism the same way the wall clock does.
A task parked on other work while it holds a permit or semaphore slot is what the lab reports as a futurelock.
There's no detached spawn. Decide which region owns the work, and that region's close will wait for it.
The default build is deliberately small. These are the features applications reach for most.
| Feature | What it enables |
|---|---|
| tls | TLS through rustls with the ring provider. Add tls-native-roots or tls-webpki-roots for root certificates. |
| http2-streaming | Live HTTP/2 request bodies with bounded queues and consumption-based flow control. |
| quic, http3 | Native QUIC and HTTP/3 (http3 implies quic). Partial: single-connection and listener paths are tested; deployment interop isn't. |
| postgres, mysql, sqlite | Database clients. PostgreSQL and MySQL speak the wire protocol directly; SQLite runs on the blocking pool. |
| io-uring | The Linux io_uring reactor (kernel 5.1+). |
| metrics, tracing-integration | OpenTelemetry metrics and tracing spans. Neither pulls in tokio. |
| tower | Adapters for tower's Service trait. |
| atp-cli | The standalone atp file-transfer binary. |
The full list, including the browser profiles and the test-only features, is in the upstream README. Migrating an existing tokio service? Start with the Tokio migration playbook and its read-only readiness planner, and the repository's agent skill if you work with Claude Code or Codex.
Including the ones with unflattering answers.