mcpbeat

Cpp Static Thread Safety

google/cpp-static-thread-safety

> Enforce C++ static thread safety annotations and correct synchronization primitives. Use this skill when designing multi-threaded classes or editing guarded member fields.

1k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
20318
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/google/filament --skill cpp-static-thread-safety

The instruction itself

7 sections, as written by the author

C++ Static Thread Safety & Synchronization Guidelines

Filament leverages Clang's static thread safety analysis to verify lock holding requirements at compile-time. All multi-threaded classes must use explicit capability annotations to guarantee race-free state access.


1. Selecting the Correct Lock Primitive

  • Filament Primitives (utils::Mutex & utils::Condition) — MANDATORY:
  • *Rule*: All engine, backend, and utility code under filament/ must use utils::Mutex and utils::Condition exclusively. Do not use std::mutex or std::condition_variable.
  • *Deadlock & Order Debugging*: utils::Mutex transparently integrates with Filament's compile-time lock debugging facility (FILAMENT_DEBUG_MUTEX / -u). When enabled, it maintains a global cycle dependency graph via BFS during lock() and try_lock(), immediately trapping lock-order inversions and self-deadlocks with exact CallStack traces. Any locks defined via std::mutex bypass this tracker entirely and remain invisible to deadlock diagnostics.
  • *Memory & Cache Hygiene*: On Android and Linux (linuxutil::Mutex), utils::Mutex is only 4 bytes (a single atomic futex word) versus 40 bytes for std::mutex. For structures allocated in large volumes or embedded in handles/fences, this 10x size reduction prevents struct bloat and maintains cache-line density.
  • *Condition Variable Support*: utils::Condition (Condition::wait / wait_until) is explicitly templated (template <typename M>) to work seamlessly with UniqueLock<utils::Mutex> for producer-consumer queues and blocking wait loops.
  • *Priority Inversion Reality*: C++ standard std::mutex (pthread_mutex_t) does not provide priority inheritance out of the box (PTHREAD_PRIO_NONE). Therefore, std::mutex offers zero priority inversion protection over utils::Mutex.

2. Lock Guard & RAII Lifetime Conventions

  • Immutability Invariant: Use LockGuard const as the default for all standard synchronized blocks to guarantee scope-bound read-only lock scopes.
    // Correct
    utils::LockGuard const lock(mLock);
  • Condition Variables Overloads: Use UniqueLock (non-const) strictly when passing locks to condition variables (wait(lock)) or when explicit .unlock() / .lock() boundaries are required for performance or deadlock prevention.
  • Lock Dependency: Any source file (.cpp) that instantiates LockGuard or UniqueLock must explicitly include the matching utility header:
    #include <utils/Mutex.h>

3. Resolving Clang's Lambda Closure Limitations

Clang evaluates C++ anonymous closures (lambdas) as separate context boundaries. Because lambdas lack capability attributes, standard condition variable waits passing local predicates (e.g., using std::ranges::all_of) will trigger false-positive thread safety errors.

To resolve this, use one of the following approved patterns:

Decorate *only* the CV wait predicate lambda operator with UTILS_NO_THREAD_SAFETY_ANALYSIS to ignore nested boundaries while preserving outer compile-time checks:

UniqueLock lock(mQueueLock);
mQueueCondition.wait(lock, [this]() UTILS_NO_THREAD_SAFETY_ANALYSIS {
    return mExitRequested || 
           (!std::ranges::all_of(mQueues, [](auto&& q) { return q.empty(); }));
});

Pattern B: Manual Loop Inlining

If the check is flat, completely inline the CV predicate as a standard while loop to bring the member variables directly into the parent function's locked scope:

UniqueLock lock(mLock);
while (mFreeSpace < requiredSize) {
    mCondition.wait(lock);
}

4. Dynamic Threading & Preprocessor Safety

  • Single-Threaded Parity: All thread safety annotations (UTILS_GUARDED_BY) are conditionally compiled out on single-threaded configurations. To prevent compile crashes when FILAMENT_SINGLE_THREADED is defined, standard annotations are gated by UTILS_HAS_THREADING in compiler.h.

How to use it

Copy the folder

Take google/cpp-static-thread-safety from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.