> Cinder 0.1.0 documentation: Threading model
> Canonical page: https://cinder.ash10.dev/docs/threading/ · Source: `docs/THREADING.md` in the engine repository (engine commit 47c9352, imported 2026-11-09)
> Index of every page for agents: https://cinder.ash10.dev/llms.txt

# Threading model

Fixed by section 4 of the brief; this page records how it is realised and which shared state exists.

## Threads

| Thread | Owner | Runs |
|---|---|---|
| Main | process | SDL event pump, window and input, surface acquire and `wgpuSurfacePresent`, ImGui, frame orchestration; also executes jobs while waiting on a counter. |
| Job workers (`cinder-worker`), one per logical core minus one | `JobSystem` (`cinder/core/jobs.mojo`), threads created by `cn_thread_create` | Everything else: culling, animation, ECS systems, asset decode, physics step orchestration. |
| Jolt workers | Jolt's own job system (`JPH_JobSystemThreadPool`) | Physics islands; contact callbacks are posted to the MPMC event queue from these threads. |
| miniaudio device thread | miniaudio | Mixing; only the shim's log and end-of-sound callbacks run there and they post events. |
| wgpu-native internal threads | wgpu | Callbacks post events (ADR-0003). Mojo never runs on them. |

## Shared state and how it is protected

| State | Mechanism |
|---|---|
| Job deques, job table, counters | Chase-Lev deque protocol with `Atomic` and explicit orderings; counters are atomic. |
| Worker parking | `cn_semaphore_*` (counting semaphore) plus an atomic idle count; `submit` posts only when a worker is parked. |
| Native callback results (wgpu, Jolt, miniaudio, SDL log) | `CnEventQueue`, a bounded lock-free MPMC ring in the shim; drained by the main thread each frame. Overflow is counted, never silent. |
| Everything else | Owned by one thread. Cross-thread hand-over happens only by submitting a job whose data the submitter keeps alive until the counter reaches zero. |

## Rules

1. Mojo code never runs inside a C callback (ADR-0003). The only Mojo functions that run on native
   threads are job-system worker entry points, which Cinder itself created.
2. A job's data pointer must stay valid until the counter it was submitted with reaches zero; the
   submitter owns that memory (arena, pool or stack with `keep`).
3. `JobSystem.reset()` is called at frame start only, when no job is queued or running.
4. No locks exist in Mojo code. If one ever becomes necessary it must be listed here with its owner
   and the invariant it protects.
