5.3.14 · D3Build Systems & Toolchain

Worked examples — Thread Sanitizer (TSan)

2,326 words11 min readBack to topic

You already met the machinery in Thread Sanitizer (TSan): each thread carries a vector clock , every synchronization event merges clocks, and a race is write + concurrent. This page does nothing new theoretically — instead it runs the machine by hand on every kind of case the topic can throw at you, so no scenario ever surprises you.

We will write clocks as (thread A's counter, thread B's counter) for two-thread cases, extending to three when needed.


The scenario matrix

Every data-race question is one cell of this grid. The examples below are labelled with the cell they hit.

# Cell (case class) Access kinds Ordering present? Race? Example
C1 Both read, no sync read + read No Ex 1
C2 Write + write, no sync W + W none Yes Ex 2
C3 Read + write, no sync R + W none Yes Ex 3
C4 Write + write, mutex ordered W + W release→acquire No Ex 4
C5 Write + read, thread join ordered W + R join edge No Ex 5
C6 Degenerate: same thread twice W + W program order No Ex 6
C7 Transitive order via 3 threads W + R No Ex 7
C8 Boundary: volatile / relaxed without ordering W + W none Yes Ex 8
C9 Real-world word problem (bank balance) W + W depends decide Ex 9
C10 Exam twist: order edge on the wrong object W + W mismatched lock Yes Ex 10

The matrix has three axes: (a) what the two accesses are (RR / RW / WW), (b) whether a happens-before edge exists, and (c) if an edge exists, does it actually connect these two accesses. Every real bug is a failure on axis (b) or (c).



Figure — Thread Sanitizer (TSan)


Figure — Thread Sanitizer (TSan)







Recall Self-test: name the deciding fact for each cell

C1 no race because ::: both accesses are reads (no write involved) C2 races because ::: two writes and (isolated clocks) C4 is safe because ::: unlock→lock on the same mutex sets C5 is safe because ::: pthread_join is a release/acquire edge C6 is safe because ::: program order orders same-thread accesses C7 is safe because ::: transitivity chains C8 races because ::: volatile performs no synchronization event C10 races because ::: the release/acquire used different mutexes