pragma.core
pragma.core is a pure-Rust, solid and high-performance multithreaded core for razed.production's game engines and frameworks.
It is the layer under an engine: it owns the threads, schedules the work of a frame and runs background I/O, so that ECS, rendering, physics and audio can be written on top of it without each inventing their own thread pool.
Status
1.0.0 — stable. All milestones M0–M6 are done: the work-stealing job system, the per-frame task graph, async tasks, observability, tuning, the hardening pass (determinism suite, benchmark gate and semver checks in CI), the 1.0 API review, complete rustdoc with an embedding checklist, and the MSRV and versioning policy. The public API follows semver: breaking changes happen only in a major release, and every change is listed in the changelog. See the roadmap.
Features
| Area | Crate | Facade |
|---|---|---|
Typed errors, generational handles (Handle, HandlePool), simulation ticks |
pragma_base |
base |
| Sync primitives: latches, counters, task labels (plus the scheduler's internal building blocks) | pragma_sync |
sync |
| Platform layer: thread names, priorities, core affinity (Windows/Linux) | pragma_sys |
sys |
Work-stealing pool: join, scope, spawn, parallel_for, parallel_reduce, priorities |
pragma_jobs |
jobs |
| Per-frame task graph with declared resource access, validated before the first frame | pragma_graph |
graph |
| Async tasks on the pool, bounded blocking pool for synchronous I/O | pragma_async |
tasks |
| Vertical slice: 100k entities, 9-system graph, background load, per-tick state hash | examples/frame_sim |
— |
Highlights:
- Help-while-wait. Every wait (
join,scope,JobHandle::join) runs other jobs, so nested parallelism never deadlocks, even on a single worker. - Panics are errors. A panicking job, graph task or future returns
JobError::Panicked/AsyncError::Panicked; the worker survives and nothing hangs. - Conflicts are caught at build time. A graph whose tasks touch the same resource
without an explicit order fails in
build()withGraphError::AmbiguousOrder. It cannot become a data race in frame 10 000. - Allocation-free frames. After the warm-up frame, a 50-task graph runs with zero heap
allocations, with or without a
tracingsubscriber. - Deterministic. Results do not depend on which thread ran what:
frame_simproduces the same per-tick hash on 1, 2 and N workers, and CI checks this on every PR. - Observable.
PoolStatssnapshots,tracingspans, optionalpuffinscopes, a debug-only watchdog for stuck jobs, and an allocation-free "which task is running here" query for panic hooks and crash reports. - Bounded memory. Input-sized allocations go through
try_reserveor a hard limit: you getGraphError::TooLarge/BaseError::OutOfMemoryinstead of an abort.
Quick look
use *;
;
;
More examples are in the crate docs (cargo doc --open -p pragma_core) and in
examples/frame_sim.
Using it
[]
= "1.0"
Depend on the pragma_core facade rather than on the individual pragma_* crates (see
Versioning).
| Feature | Default | Enables |
|---|---|---|
jobs |
✅ | ThreadPool and the platform layer (sys) |
graph |
✅ | FrameGraph, Resources (implies jobs) |
async |
✅ | Executor, AsyncHandle (implies jobs) |
puffin |
— | Profiler scopes for pool operations and graph tasks; re-exports puffin |
base and sync are always available. Each feature builds and passes clippy on its own
(cargo hack --each-feature in CI).
MSRV policy
The minimum supported Rust version is 1.88 (rust-version in [workspace.package],
DECISIONS #10), the minimum required by proptest.
- Checked in CI. The
MSRV (1.88)job runscargo +1.88 check --workspace --all-featureson every PR and every push tomain. Development itself uses the toolchain pinned inrust-toolchain.toml(1.99.0), so lints are identical locally and in CI. - Raised only in a minor release. A higher MSRV is not treated as a breaking change, but
it never ships in a patch release: it goes into the next minor version (
1.x.0;0.x.0before 1.0), with its own line in the changelog and a decision record explaining why (DECISIONS #44). - One change, three places.
rust-version, the CImsrvjob and the badge above move together in the same PR.
Versioning
pragma.core follows SemVer; the rules are in docs/reliability.md §8.
- Lockstep. All crates share one version (
versionin[workspace.package], currently 1.0.0): thepragma_corefacade exposes all of them, so separate versions would give users nothing (DECISIONS #33). A breaking change in any crate is a breaking release of all of them. - Exact internal dependencies. The crates depend on each other with
=X.Y.Z, sopragma_jobs1.0.0 never builds against apragma_sync1.0.1 whose internals have moved (DECISIONS #42). Depend onpragma_core, or keep allpragma_*crates at the same version. - Outside semver. No compatibility guarantees cover:
pragma_sync::__private(hidden from the docs): the scheduler's building blocks, which change with every optimization ofjoin/scope;- the
puffinfeature and thepragma_core::puffinre-export:puffinis not 1.0 yet, and moving to a newpuffinversion is a minor release.
- Breaking changes. Before 1.0, a minor version (
0.x.0) may break the API, and every break is listed in the changelog. After 1.0, breaks happen only in a major release; an item is first marked#[deprecated(since, note)]for at least one minor release. - Checked in CI.
cargo semver-checks --workspaceruns on every PR against the PR's base onmain(not against crates.io or a tag): a breaking change that comes without a version bump fails the PR that introduces it.
Performance
Windows 10, AMD FX-8350 (8 logical cores), cargo bench. Ranges are min…max across A/B
rounds. Full method and numbers are in docs/perf.md.
| Benchmark | pragma.core | rayon |
|---|---|---|
single join |
284…288 ns | 1.47…1.51 µs |
fib(20), ~11k nested join |
197…200 µs | 99…108 µs |
parallel_for, 10⁶ elements |
454…478 µs | 477…508 µs |
parallel_reduce, 10⁶ elements |
198…214 µs | 230…282 µs |
| frame graph, 50 near-empty tasks | 8.3…8.4 µs per frame | — |
frame_sim, 100k entities |
~5 ms/tick on 1 worker, ~2 ms on 11 | — |
Nested fork-join is the one place where rayon is still ahead (about 2×). A CI gate fails any PR that regresses a benchmark by more than 10% beyond runner noise.
Reliability
Reliability here is a set of properties that CI checks on every PR. Details are in docs/reliability.md.
- Pure Rust. No C/C++ compiled into the dependency tree; enforced by
cargo denyandtools/audit_workspace.py. System FFI (windows-sys,libc) only. - No panics in the runtime.
unwrap/expect/panic!/unchecked indexing are denied by lints; failures are#[non_exhaustive]typed errors with context. - Contained
unsafe. Only inpragma_sync(lifetime erasure, on-stack jobs, task labels) andpragma_sys(OS calls). Every occurrence has a budget inlayers.toml; any growth needs a new decision record. All of it is reviewed in docs/unsafe.md. - Verified concurrency. Every atomic protocol has a loom model.
pragma_syncruns under Miri, and a one-worker pool runs under Miri with Tree Borrows. - Tested in depth. Unit, property (
proptest) and stress tests, plus fuzzing of the graph builder against an independent oracle. Nightly CI runs 30-minute fuzzing and soak tests. - Layered. Each crate depends only on lower layers (
layers.toml):
L4 pragma_core facade: re-exports, prelude, features
L3 pragma_graph frame graph pragma_async async tasks
L2 pragma_jobs work-stealing pool
L1 pragma_sync sync primitives pragma_sys platform layer
L0 pragma_base errors, handles, ticks
Platforms
Windows and Linux (x86_64) are first-class and tested in CI. Other targets are not supported yet; the API does not rule out a single-threaded mode for wasm later. See DECISIONS.md.
Building
The toolchain is pinned in rust-toolchain.toml (1.99.0); the minimum supported Rust version
is 1.88 (MSRV policy). See CONTRIBUTING.md for the full check list (Miri, loom, fuzz, benches).
Documentation
- API reference: docs.rs/pragma_core (or
cargo doc --open -p pragma_core). Every public type and free function has an example; the crate page ofpragma_corehas the section "Integrating into an engine": pool ownership, the graph in the game loop, async results, priorities, panics, shutdown order and determinism - Architecture: layers and the execution model
- Reliability rules
- Performance methodology and results
unsafereview- Roadmap
- Decision log
- Changelog
- Contributing
License
MIT © 2026 razed