Source code
Revision control
Copy as Markdown
Other Tools
# Threading & concurrency — dom/media review guidance
> Posture is **adversarial about scheduling** — assume any other thread can run at
> the worst possible moment between two of your instructions. A confirmed data race
> or cross-thread use-after-free is the highest-priority class of finding.
## General Gecko threading
- **Right-thread assertions.** Operations run on the thread they require, and the
code asserts it (`MOZ_ASSERT(NS_IsMainThread())` / `AssertIsOnMainThread()` /
the object's own `AssertOnTaskQueue()`), accurately.
- **Prefer compile-time thread-safety annotations.** For lock-guarded state,
Gecko's Clang thread-safety analysis catches misuse at build time: annotate the
member `MOZ_GUARDED_BY(mMutex)` and functions `MOZ_REQUIRES(...)` /
`MOZ_EXCLUDES(...)`. New shared state that relies only on a runtime assert (or
nothing) where a static annotation would fit is worth flagging.
- **Shared state is synchronized.** Any field read on one thread and written on
another is protected by a mutex/monitor or is atomic. A read/write pair with no
synchronization is a **data race**.
- **Race / TOCTOU.** A value checked and then used after the lock is dropped; a
"check-then-act" where the state can change in between; a non-atomic
read-modify-write on shared state.
- **Re-entrancy.** A call that can re-enter the same object or lock — via a
listener, observer, callback, or an event-loop/`SpinEventLoop` — and corrupt
half-updated state or self-deadlock.
- **Lock discipline.** Lock acquisition order is consistent (no inversion ->
deadlock); a lock is not held across a re-entrant call, a dispatch, or a
blocking wait.
- **Dispatch correctness.** Runnables target the right queue/thread; captured
state is kept alive and is safe to touch on the target thread; nothing is
dispatched to (or run on) a thread/TaskQueue after it has been shut down.
## dom/media specifics
- **TaskQueue / `AbstractThread` model.** Most of the media pipeline runs off the
main thread on TaskQueues (the decoder task queue, the MDSM task queue, the
demuxer task queue). A method that must run on a specific queue should assert it
(`AssertOnTaskQueue()` / `MOZ_ASSERT(OnTaskQueue())`), and members owned by one
queue must not be touched from another without dispatching. For *compile-time*
queue/thread affinity, dom/media uses `EventTargetCapability<T>` (a
`MOZ_CAPABILITY`): annotate queue-affine members `MOZ_GUARDED_BY(mThread)` and
methods `MOZ_REQUIRES(mThread)` (as in `AudioSinkWrapper` and
`TrackBuffersManager`), so a cross-queue access fails to compile rather than
only tripping a runtime assert. Prefer it for new queue-affine state.
- **MediaDecoderStateMachine.** State reads/writes and transitions happen on the
MDSM's own queue; watch for logic that inspects or mutates MDSM state from a
different thread, and for re-entrancy through watchers/mirrors.
- **Single-TaskQueue decoders.** Decoders are wrapped in `MediaDataDecoderProxy`,
which enforces single-TaskQueue use — decoder calls must all stay on that one
queue.
- **Real-time audio threads.** Work on the cubeb audio callback and on the
`GraphRunner` (which drives the MediaTrackGraph) must be real-time-safe — no
locks that can be held by other threads, no allocation, no blocking. Flag
locking/allocation introduced on either path.
- **No blocking the main thread.** Synchronous waits on a media operation from the
main thread (or main-thread I/O) are a finding.