Katkat

Coordinate Counter Updates Across Threads

TeslaSoftware Engineer · Onsite

Last updated

Tests multi-threaded synchronization, atomic operations, barrier synchronization, and coordinated condition handling across concurrent threads.

Problem Statement

In concurrent telemetry collection systems, data streams often require snapshotting at both milestone thresholds (event-driven) and fixed intervals (time-driven).

You are tasked with coordinating two worker threads that concurrently increment a shared counter starting at 0.

Implement a class or runner function that coordinates two threads, Thread-A and Thread-B, with the following requirements:

  1. Synchronized Launch: Neither thread may begin incrementing until both threads are initialized and ready to start simultaneously. The launch timestamp must be recorded at this synchronization point.
  2. Shared State: Both threads increment the same shared counter. Every increment must be thread-safe (no lost updates).
  3. Event-Driven Snapshot (Thread-A): Thread-A monitors the counter value. When the counter reaches or exceeds target_count (e.g., 10), Thread-A records snapshot_A with the observed counter value.
  4. Time-Driven Snapshot (Thread-B): Thread-B takes snapshot_B of the counter value exactly when delay_sec (e.g., 1.0 second) has elapsed relative to the synchronized launch time.
  5. Graceful Shutdown: Once both snapshots have been taken, both threads must terminate cleanly, and the function must return the combined results.

Function Signature

from typing import Tuple

def run_coordinated_counter(target_count: int = 10, delay_sec: float = 1.0) -> Tuple[int, int, int]:
    """
    Coordinates two threads incrementing a shared counter.
    
    Returns:
        (snapshot_a, snapshot_b, final_counter_val)
    """
    pass

Constraints

  • target_count >= 1
  • 0.05 <= delay_sec <= 5.0
  • All increments and reads must be synchronized to prevent race conditions.
  • Neither thread may spin indefinitely after its required snapshot is completed.

Example

snapshot_a, snapshot_b, final_val = run_coordinated_counter(target_count=10, delay_sec=0.2)

# snapshot_a >= 10 (the value Thread-A observed when threshold reached)
# snapshot_b >= snapshot_a (or vice-versa depending on speed, but >= 0)
# final_val >= max(snapshot_a, snapshot_b)

Explanation: Both threads start at T0. The shared counter increases rapidly. Thread-A observes counter >= 10 and stores snapshot_a = 10 (or slightly higher if another thread incremented concurrently). Meanwhile, at T0 + 0.2s, Thread-B records the current counter value as snapshot_b. Once both conditions are fulfilled, both threads exit, and the final state is returned.

Edge Cases to Consider

  • The counter reaching target_count before or after delay_sec expires.
  • Ensuring one thread does not terminate and leave the shared lock in an inconsistent state.
  • Ensuring worker loops yield or pause appropriately so CPU usage does not starve other system threads.

Hints

Hint 1

Use a synchronization barrier (such as `threading.Barrier` in Python) to ensure both threads launch at the exact same instant.

Hint 2

Use a mutual exclusion lock (`threading.Lock`) or an atomic integer primitive to protect the counter increments and reads from data races.

Hint 3

Use a shared event (`threading.Event`) or boolean flags under the lock to signal when both snapshots have been recorded so both worker loops can terminate cleanly.

Follow-up questions

  • How would you guarantee that Thread-B takes its snapshot with millisecond-level precision without burning 100% CPU in a busy-wait loop?
  • If Python's GIL were removed (or in C++/Java), how would you replace the mutex lock with atomic primitives like `compare_and_swap` or `fetch_add`?
  • How would you scale this coordination pattern from 2 threads to N worker threads with multiple heterogeneous snapshot triggers?