Read XcodeToolingCheatSheet.md (Run scheme and diagnostics, Debugger UI) if Thread Sanitizer or sanitizer reports / stack frames are unfamiliar.
Post-MVP / scheme diagnostic. Thread Sanitizer (TSan) catches data races: two threads accessing the same memory where at least one is a write, without proper synchronization.
Source of truth: SignalLab/SignalLab/Shared/LabDomain/LabCatalog.swift (threadSanitizerLab)
How do I prove unsafe concurrent access instead of guessing from intermittent wrong UI?
- Wrong value sometimes under rapid taps, background work, or parallel tasks.
- Not reliably explained by a single wrong branch (Breakpoint Lab) or a synchronous main-thread block (Hang Lab).
Xcode Run → Diagnostics → Thread Sanitizer (expect slower runs).
| Symptom | First tool |
|---|---|
| Wrong branch, deterministic | Breakpoint Lab |
| UI frozen, main thread busy | Hang Lab |
| Slow but responsive | CPU Hotspot Lab + Time Profiler |
| Concurrent unsynchronized memory | Thread Sanitizer |
- Enable Thread Sanitizer in the Run scheme, then launch from Xcode.
- Open Thread Sanitizer Lab and tap Run scenario — the lab races a shared counter between the main thread and a detached task with no lock. Run until TSan stops.
- From the report: identify both threads, the address, and your frames.
- Add serialization (main actor, lock, serial queue, actor) or remove shared mutable state.
- Re-run with TSan enabled until that path is clean; apply the same idea in your code.
- TSan enabled for the repro run.
- You can name the shared state and the two conflicting accesses.
- You can explain why Breakpoint Lab would not be the right primary tool.