Skip to content

Concurrency model

Yamete builds with Swift 6 complete strict concurrency, no @unchecked Sendable escape hatches except for the IOKit boundary types that physically cannot be made Sendable. The bus is an actor so fan-out is serialised. Sources run on their own queues / AsyncStream continuations and hop to the bus actor for publish. Outputs run on @MainActor only when they touch AppKit (screen flash window pool, status item face swap); LED PWM and audio playback do not.

Three different thread contexts produce sensor data for the impact pipeline. Each sensor implementation serializes its own hardware-callback state with an OSAllocatedUnfairLock, then yields to an AsyncThrowingStream outside that lock. ImpactFusion fans those streams into a private AsyncStream via detached tasks, then drains on a MainActor task. Infrastructure event sources use run-loop callbacks or dispatch-main-queue callbacks and also publish asynchronously to the bus. The bus is an actor; subscribers iterate their AsyncStream<FiredReaction> on MainActor tasks.

      Thread and actor boundaries

A subtle constraint: continuation.finish() and continuation.yield() must be called outside each sensor's OSAllocatedUnfairLock. AsyncThrowingStream._Storage acquires its own os_unfair_lock inside those methods, and os_unfair_lock is non-reentrant. calling yield or finish while already holding an unfair lock abort-traps (cause 89859). Each sensor captures the continuation reference as the critical section's return value, releases the lock, then calls the stream method outside the lock.

Engineered with excessive care for a deeply unnecessary purpose.