Plast interview

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.