36. Racing Counter
In a multi-core feed handler, a shared counter is updated on every packet. Latency is paramount, but silent concurrency bugs can corrupt state. Evaluate behavior using sanitizers under typical HFT build flags.
int x = 0;
void w() { for (int i=0;i<100000;i++) x++; }
int main() {
std::thread t(w);
w();
t.join();
}
Part 1.
What will ThreadSanitizer report, and why? Provide a minimal change to make the program race-free without locks, noting the expected overhead.
Part 2.
(1) ASan, UBSan, TSan: which applies here and why?
(2) How to reduce TSan overhead in CI pipelines for hot code?
(3) When is memory_order_relaxed insufficient for a shared counter?
(4) Can TSan miss races with atomics or intrinsics? Explain scenarios.
(5) How do sanitizers interact with -O3, LTO, and inlining decisions?
Answer
Answer (Part 1)
TSan reports a data race: unsynchronized writes to x from two threads; this is UB under the memory model. Minimal fix: use an atomic increment, e.g., change to std::atomic<int> x{0}; and in w() do x.fetch_add(1, std::memory_order_relaxed); to avoid ordering costs; overhead is the atomic RMW latency but still lock-free.
Answer (Part 2)
(1) TSan applies; it detects data races. ASan/UBSan typically report nothing here unless other memory/UB issues exist.
(2) Use targeted TSan builds, per-test instrumentation, TSAN_OPTIONS=report_bugs=1,history_size=0, and run reduced workloads on critical subsets.
(3) When readers need ordering with other data, counters drive visibility, or correctness depends on sequencing; use acquire/release then.
(4) Yes. Races using atomics with improper fences, custom allocators, or hand-rolled atomics via intrinsics can evade TSan’s instrumentation.
(5) Sanitizers insert instrumentation before optimization but cooperate with -O3 and LTO. Inlining may change stack traces; correctness of reports remains.