ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
Topics
sre111localhost lab102performance70python58javascript47bundle size36benchmarking35node.js35web development21typescript19benchmark16content creation15All topics
About
Loading article…

ShopperCove

Hands-on notes on developer tools, written after the tool has actually been run.

All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove

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
  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.

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:

  • 01shoppercove.comcmath vs math hypot localhost lab
  • 02shoppercove.comsqlite3 vs shelve localhost lab
  • 03shoppercove.comgc collect cost localhost lab
  • 04shoppercove.comcopy copy vs dict copy localhost lab
  • 05shoppercove.comhtml parser vs regex localhost lab
  • 06shoppercove.comdataclass asdict vs vars localhost lab
  • 07shoppercove.comtracemalloc snapshot localhost lab
  • 08shoppercove.comintenum 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.

Keep reading

  • 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.

    Summary1 Oct 20264 min read

  • 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.

    Summary1 Oct 20263 min read

  • tracemalloc Snapshot Cost: Localhost Lab

    Summary1 Oct 20264 min read