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.
4 min read
Wake latency with threading.Event vs a SIGUSR1 self-signal handler, plus signal.signal install cost. Measured on Linux localhost.
Related links:
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.
| 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).
Script: lab-evidence/135-signal-vs-event-wakeup/results/run_lab.py.
| 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 |
threading.Event (or Condition/Queue) — safe and idiomatic.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.”
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.
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.
pause()/sigwait.Evidence: summary.json, summary.txt.
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.
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
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.
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.
Summary1 Oct 20264 min read
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.
Summary1 Oct 20263 min read
Summary1 Oct 20264 min read