Plate 50
signal vs threading.Event Wakeup: Localhost Lab
Hands-on signal SIGUSR1 vs threading.Event wakeup lab: real p50 latency in microseconds, measured on Linux localhost today in this hands-on lab for SREs.
Aditya Challa4 min read
Intro — what this post promises
Wake latency with threading.Event vs a SIGUSR1 self-signal handler, plus signal.signal install cost. Measured on Linux localhost.
Related links:
- cmath vs math hypot localhost lab
- sqlite3 vs shelve localhost lab
- gc collect cost localhost lab
- copy copy vs dict copy localhost lab
- html parser vs regex localhost lab
- dataclass asdict vs vars localhost lab
- tracemalloc snapshot localhost lab
- intenum vs int localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. These arms are not identical models: Event wakes a worker thread; SIGUSR1 runs a main-thread handler with a busy-wait until delivery. Numbers show order-of-magnitude wakeup cost, not a drop-in API swap.
Verdict up front (n=200 wakes): Event p50 ~11.2 µs; SIGUSR1 self-kill p50 ~2.2 µs. Handler install ~1092573 ops/s.
Arms
| Arm | Pattern |
|---|---|
| Event.wait / set | worker thread wakeup |
| os.kill(SIGUSR1) + handler | same-process signal |
| signal.signal install loop | registration cost |
Seven rounds for install; wake arms use median of 200 one-shot latencies (complete=True).
Lab topology
Script: lab-evidence/135-signal-vs-event-wakeup/results/run_lab.py.
Lead table
| Arm | p50 | notes |
|---|---|---|
| threading.Event | 11.2 µs | min 6.5 / max 416.9 |
| SIGUSR1 self-kill | 2.2 µs | min 1.8 / max 30.2 |
| signal.signal install | 1092573 ops/s | registration only |
Reading it for SRE work
- Cross-thread coordination in apps →
threading.Event(or Condition/Queue) — safe and idiomatic. - Integrating with C extensions / process supervision that already speak UNIX signals → measure handler work; keep handlers tiny.
- Do not replace Events with signals for normal Python concurrency — signal delivery rules (main thread only) will bite.
- Install cost (~1092573 ops/s) is cheap; handler body and reentrancy are the risk.
Methodology caveat
The signal arm busy-waits until the handler clears a flag, so it excludes scheduler sleep and can look faster than Event (~2.2 µs vs ~11.2 µs). Event includes thread wake scheduling. Treat this as “how expensive is a self-signal round trip vs an Event set,” not “signals beat threads.”
Operational guidance
Use signals for external process control (reload, graceful stop). Use Events for in-process wait/notify. Mixing them without a clear ownership map creates lost wakeups and main-thread-only surprises under load.
Keep signal handlers under a few microseconds of safe work — set a flag, write a byte to a wakeup fd — and do real processing on a normal thread. That discipline matters more than the ~2.2 µs self-kill number in this lab.
Install vs delivery
Rebinding signal.signal ran at ~1092573 ops/s — registration is not the bottleneck. Delivery semantics and handler safety are. Pair this lab with your process manager’s reload story: a SIGHUP that runs a full config parse in-handler will dominate any ~2.2 µs self-kill figure.
Pitfalls
- Doing heavy work inside signal handlers.
- Assuming signal latency from this busy-wait model applies to
pause()/sigwait. - Forgetting main-thread-only delivery in CPython.
- Comparing to lab 23 epoll/select I/O multiplexing (different problem).
Reproduce
Evidence: summary.json, summary.txt.
Limits
One Linux box, same-process only. Not multiprocess kill storms, not realtime OS guarantees.
When in doubt, wake a queue or write to a self-pipe and keep the handler trivial.
Takeaway
Event wakeup p50 ~11.2 µs; SIGUSR1 self-signal p50 ~2.2 µs under a busy-wait delivery check. Prefer Event for Python thread coordination; reserve signal for process-level control with tiny handlers.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. Event p50 11.2 us; SIGUSR1 self-kill p50 2.2 us; install 1092573 ops/s. Different wakeup models — see body. Affiliates: 0. Evidence: lab-evidence/135-signal-vs-event-wakeup/results/summary.json. Results reproduced on Linux localhost today.
Related links
Plate 17
platform vs os.uname Inventory: Localhost Lab
Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 76
cmath vs math.hypot Magnitudes: Localhost Lab
Hands-on cmath vs math.hypot magnitude ops lab: real ops/s for abs, polar, and phase, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 51
tracemalloc Snapshot Cost: Localhost Lab
1 Oct 2026