No description
  • C 89.8%
  • Makefile 10.2%
Find a file
Tej Chajed 5ced2c3f46
Remove pthread_setname_np
TSan doesn't show these thread names (it always uses numeric ids)
2026-03-12 11:17:23 -05:00
.gitignore Add demo after cleanup 2026-03-08 17:57:12 -05:00
Makefile Simplify Makefile 2026-03-12 11:07:51 -05:00
README.md Add README and thread names 2026-03-12 11:14:29 -05:00
tsan_demo.c Remove pthread_setname_np 2026-03-12 11:17:23 -05:00
tsan_fixed.c Remove pthread_setname_np 2026-03-12 11:17:23 -05:00

ThreadSanitizer demo

tsan_demo.c has a race condition which we can use ThreadSanitizer (TSan) to detect.

Run make then ./tsan_demo.

Example TSan Output

$ ./tsan_demo

Output:

==================
WARNING: ThreadSanitizer: data race (pid=73303)
  Write of size 4 at 0x000102978000 by thread T2:
    #0 increment_thread tsan_demo.c:20 (tsan_demo:arm64+0x100000690)

  Previous write of size 4 at 0x000102978000 by thread T1:
    #0 increment_thread tsan_demo.c:20 (tsan_demo:arm64+0x100000690)

  Location is global 'shared_counter' at 0x000102978000 (tsan_demo+0x100008000)

  Thread T2 (tid=49145052, running) created by main thread at:
    #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708)
    #1 main tsan_demo.c:54 (tsan_demo:arm64+0x1000007f8)

  Thread T1 (tid=49145051, finished) created by main thread at:
    #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708)
    #1 main tsan_demo.c:54 (tsan_demo:arm64+0x1000007f8)

SUMMARY: ThreadSanitizer: data race tsan_demo.c:20 in increment_thread
==================
==================
WARNING: ThreadSanitizer: data race (pid=73303)
  Read of size 4 at 0x000102978008 by thread T4:
    #0 reader_thread tsan_demo.c (tsan_demo:arm64+0x100000728)

  Previous write of size 4 at 0x000102978008 by thread T3:
    #0 writer_thread tsan_demo.c:31 (tsan_demo:arm64+0x1000006e4)

  Location is global 'ready' at 0x000102978008 (tsan_demo+0x100008008)

  Thread T4 (tid=49145106, running) created by main thread at:
    #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708)
    #1 main tsan_demo.c:68 (tsan_demo:arm64+0x100000894)

  Thread T3 (tid=49145105, finished) created by main thread at:
    #0 pthread_create <null> (libclang_rt.tsan_osx_dynamic.dylib:arm64e+0x2f708)
    #1 main tsan_demo.c:67 (tsan_demo:arm64+0x10000087c)

SUMMARY: ThreadSanitizer: data race tsan_demo.c in reader_thread
==================
=== ThreadSanitizer Demo ===
This program contains intentional data races.
Demo 1: Counter increment race
Expected: 400000, Got: 400000
Demo 2: Read-Write race
Reader shared_data: 42

=== Demo Complete ===
TSan should have reported data races above.
ThreadSanitizer: reported 2 warnings

What This Tells Us:

  1. Both data races detected, in the two demos in that file.
  2. Conflicting accesses identified:
    • Thread T1 and T2 both write to shared_counter at the same time due to an increment
    • Thread T3 writes to ready while Thread T4 reads it without synchronization
  3. Exact locations: Line 20 in tsan_demo.c (the shared_counter++ statement) and line 31 (ready flag)
  4. Result may be correct by luck — the counter shows 400000 here but the race still exists

Running the Fixed Version

$ ./tsan_fixed

Output:

=== ThreadSanitizer Demo - FIXED ===
This program uses proper synchronization.
Compile with: gcc -fsanitize=thread -g -o tsan_fixed tsan_fixed.c -lpthread

Demo 1: Safe counter increment
Expected: 400000, Got: 400000
Demo 2: Safe Read-Write
Reader saw: 42

=== Demo Complete ===
Done. TSan should report NO data races.

What This Tells Us:

  1. Zero warnings - no data races detected
  2. Correct results - counter matches expected value consistently
  3. Proper synchronization using mutexes prevents races

Data races

What makes these bugs data races?

Two or more threads accessing the same memory location where:

  1. At least one access is a write
  2. Accesses are not ordered by synchronization (mutex, atomic, etc.)
  3. Accesses could happen simultaneously

How to interpret TSan output:

WARNING: ThreadSanitizer: data race
  Read of size 4 at 0x... by thread T2:     <- Second access (Read)
    #0 function_name file.c:LINE            <- Where in code

  Previous write of size 4 at 0x... by thread T1: <- First access (Write)
    #0 function_name file.c:LINE            <- Where in code

  Location is global 'variable_name'        <- Which variable

The key information:

  • What kind of data race? (read/write vs write/write)
  • Where in the code? (file and line number for each thread)
  • Which address? (global variable or other shared memory)