ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 50

  1. Blog

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 Challa·1 October 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table
  5. Reading it for SRE work
  6. Methodology caveat
  7. Operational guidance
  8. Install vs delivery
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway

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

ArmPattern
Event.wait / setworker thread wakeup
os.kill(SIGUSR1) + handlersame-process signal
signal.signal install loopregistration cost

Seven rounds for install; wake arms use median of 200 one-shot latencies (complete=True).


Lab topology

n_wakes=200 · p50 latency
install: n=20000 × 7 rounds

Script: lab-evidence/135-signal-vs-event-wakeup/results/run_lab.py.


Lead table

Armp50notes
threading.Event11.2 µsmin 6.5 / max 416.9
SIGUSR1 self-kill2.2 µsmin 1.8 / max 30.2
signal.signal install1092573 ops/sregistration 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

python3 lab-evidence/135-signal-vs-event-wakeup/results/run_lab.py

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.

signalsigusr1threading.eventwakeuppythonlocalhost labsre

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.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

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

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table
  5. Reading it for SRE work
  6. Methodology caveat
  7. Operational guidance
  8. Install vs delivery
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove