Poll Timeout Guide¶
Understanding Manager.poll(timeout_ms) and choosing the right timeout value.
What is poll()?¶
poll() drives the Mongoose event loop. It processes network events (connections, reads, writes) and returns after either:
- Processing all pending events, OR
- The timeout expires (whichever comes first)
manager = Manager(handler)
manager.listen('http://0.0.0.0:8000', http=True)
while True:
manager.poll(timeout_ms) # Process events for up to timeout_ms
Choosing the Right Timeout¶
Common Values¶
| Timeout | Use Case | Pros | Cons |
|---|---|---|---|
0 |
High CPU usage, minimal latency | Instant response | 100% CPU usage (busy loop) |
10-50ms |
Real-time applications | Very responsive | Higher CPU usage |
100ms |
Recommended default | Good balance | Slight shutdown delay |
500-1000ms |
Low-priority background tasks | Low CPU usage | Slow Ctrl+C response |
Recommended: 100ms¶
Why 100ms?
- OK Responsive Ctrl+C (exits in ~100ms)
- OK Minimal CPU overhead (~0.1% idle CPU)
- OK Fast enough for HTTP servers (sub-millisecond latency still achieved)
- OK Works well for most applications
Shutdown Responsiveness¶
The timeout affects how quickly Ctrl+C is handled:
try:
while True:
manager.poll(timeout_ms) # KeyboardInterrupt checked HERE
except KeyboardInterrupt:
print("Shutting down...")
KeyboardInterrupt is only caught between poll calls, so:
poll(100)→ Ctrl+C takes ~100ms to respond OKpoll(1000)→ Ctrl+C takes ~1 second to respond BADpoll(5000)→ Ctrl+C takes ~5 seconds to respond BADBAD
Example: Slow Shutdown¶
Example: Responsive Shutdown¶
Performance Impact¶
Good news: Poll timeout has minimal performance impact on throughput!
Benchmark Results¶
All tests with wrk -t4 -c100 -d10s:
| Timeout | Requests/sec | Latency | Shutdown Time |
|---|---|---|---|
| 0ms (busy loop) | 89,090 | 1.12ms | Instant |
| 50ms | 88,900 | 1.13ms | ~50ms |
| 100ms | 88,710 | 1.13ms | ~100ms OK |
| 500ms | 88,590 | 1.14ms | ~500ms |
| 1000ms | 88,500 | 1.14ms | ~1000ms |
Key insight: Even 1000ms timeout only reduces throughput by 0.6% because:
- Under load, there are always pending events
poll()returns early when events are ready- The timeout only matters when idle
When Timeout Matters¶
The timeout primarily affects idle servers:
- Under load: poll() returns immediately with pending events
- Idle: poll() waits full timeout before checking for shutdown signals
Special Cases¶
1. Ultra-Low Latency Requirements¶
For sub-millisecond latency requirements:
Benchmark shows diminishing returns below 50ms.
2. Background Services¶
For low-priority background tasks:
import signal
shutdown = False
def signal_handler(signum, frame):
global shutdown
shutdown = True
print("\nShutdown signal received")
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
while not shutdown:
manager.poll(1000) # Longer timeout OK with signal handler
3. Multi-threaded with Wakeup¶
If using manager.wakeup() from another thread:
import threading
stop_flag = threading.Event()
def run_server():
while not stop_flag.is_set():
manager.poll(1000) # Longer timeout OK, wakeup() interrupts
def shutdown_server():
stop_flag.set()
manager.wakeup() # Interrupt poll() immediately
CPU Usage¶
Poll timeout affects CPU usage when idle:
| Timeout | Idle CPU | Under Load CPU |
|---|---|---|
| 0ms | 100% (busy loop) | ~50-80% |
| 10ms | ~5-10% | ~50-80% |
| 100ms | ~0.1% | ~50-80% |
| 1000ms | ~0.01% | ~50-80% |
Under load, CPU usage is dominated by request processing, not poll overhead.
Best Practices¶
OK DO¶
# Standard server
while True:
manager.poll(100) # Good default
# With proper cleanup
try:
while True:
manager.poll(100)
except KeyboardInterrupt:
print("Shutting down...")
finally:
manager.close() # Clean up resources
# With signal handler for production
import signal
shutdown = False
signal.signal(signal.SIGINT, lambda s, f: globals().update(shutdown=True))
signal.signal(signal.SIGTERM, lambda s, f: globals().update(shutdown=True))
while not shutdown:
manager.poll(100)
BAD DON'T¶
# DON'T: Busy loop wastes CPU
while True:
manager.poll(0) # BAD 100% CPU even when idle
# DON'T: Slow shutdown response
while True:
manager.poll(5000) # BAD Takes 5 seconds to exit
# DON'T: Forget exception handling
while True:
manager.poll(100) # BAD No Ctrl+C handling
Summary¶
- Recommended default:
poll(100)- best balance of responsiveness and CPU usage - Performance: Timeout has minimal impact on throughput (< 1% difference)
- Shutdown: Use shorter timeouts (10-100ms) for responsive Ctrl+C
- CPU usage: Only matters when idle; under load, always busy processing events
- Production: Consider signal handlers for graceful SIGTERM handling
TL;DR: Use manager.poll(100) for most applications. It's fast, responsive to Ctrl+C, and CPU-efficient.