Fix Thread-Safe Trigger Synchronization
PurestorageSoftware Engineer · Technical ScreenLast updated
Tests understanding of concurrency primitives, condition variables, thread synchronization, and memory visibility by designing a thread-safe one-shot trigger coordinating multiple waiting threads.
Problem Statement
In concurrent systems, worker threads often need to wait for a shared initialization step, external event, or shutdown signal before proceeding. A common pattern for this coordination is a One-Shot Trigger (similar to a countdown latch of size 1 or an event flag).
Implement a thread-safe OneShotTrigger class that coordinates multiple waiting threads and one or more firing threads with the following specifications:
fire(): Transitions the trigger into the "fired" state and unblocks all currently waiting threads. Subsequent calls tofire()should be safe, idempotent, and have no additional effect.wait(timeout: float = None) -> bool: Blocks the calling thread until the trigger has been fired. Iftimeoutis provided (in seconds, non-negative), it blocks for at mosttimeoutseconds. ReturnsTrueif the trigger was fired (either before the call or during the wait); returnsFalseif the wait timed out before the trigger was fired.is_fired() -> bool: ReturnsTrueiffire()has been called,Falseotherwise, without blocking.
Your implementation must be thread-safe, avoid busy-waiting, properly guard against spurious wakeups, and ensure that threads calling wait() after fire() has already occurred return immediately with True.
Interface
class OneShotTrigger:
def __init__(self):
pass
def fire(self) -> None:
"""Fires the trigger and releases all waiting threads."""
pass
def wait(self, timeout: float = None) -> bool:
"""Waits for the trigger to be fired or until timeout seconds elapse.
Returns True if fired, False if timed out.
"""
pass
def is_fired(self) -> bool:
"""Returns True if trigger is fired, False otherwise."""
pass
Examples
Example 1: Basic wait and fire
trigger = OneShotTrigger()
results = []
def worker():
triggered = trigger.wait()
results.append(triggered)
t = threading.Thread(target=worker)
t.start()
time.sleep(0.05)
trigger.fire()
t.join()
assert results == [True]
assert trigger.is_fired() is True
Example 2: Timeout expiration
trigger = OneShotTrigger()
# No fire() called; wait should time out after 0.1 seconds
result = trigger.wait(timeout=0.1)
assert result is False
assert trigger.is_fired() is False
Constraints & Edge Cases
- Any number of threads may call
wait(),fire(), andis_fired()concurrently. timeoutcan beNone(wait indefinitely) or a float>= 0.0. Iftimeout == 0.0, return immediately based on the current state without blocking.- Ensure condition variable predicate checks handle spurious wakeups correctly.
- Avoid race conditions where a thread registers a wait right as
fire()is broadcasting.
Hints
Hint 1
Use a mutex (lock) alongside a condition variable to protect the boolean state flag and safely coordinate notifications.
Hint 2
Remember that condition variables can wake up spuriously; always check the predicate in a loop and recalculate remaining timeout on each wake.
Hint 3
Ensure that `fire()` uses `notify_all()` (or `broadcast()`) so all waiting threads wake up, not just a single thread.
Follow-up questions
- How would you modify this class to support a thread-safe `reset()` method (turning it into a reusable barrier/event), and what race conditions arise between `reset()`, `fire()`, and pending waiters?
- If your language does not provide high-level condition variables, how would you implement this primitive using low-level atomic operations and OS futexes/semaphores?
- How can you ensure memory visibility and prevent compiler or CPU instruction reordering when inspecting `is_fired()` without acquiring the lock?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.