Revision 2d6b5414 lib/daemon.py
b/lib/daemon.py | ||
---|---|---|
457 | 457 |
self.out_socket.send("\0") |
458 | 458 |
|
459 | 459 |
|
460 |
class _ShutdownCheck: |
|
461 |
"""Logic for L{Mainloop} shutdown. |
|
462 |
|
|
463 |
""" |
|
464 |
def __init__(self, fn): |
|
465 |
"""Initializes this class. |
|
466 |
|
|
467 |
@type fn: callable |
|
468 |
@param fn: Function returning C{None} if mainloop can be stopped or a |
|
469 |
duration in seconds after which the function should be called again |
|
470 |
@see: L{Mainloop.Run} |
|
471 |
|
|
472 |
""" |
|
473 |
assert callable(fn) |
|
474 |
|
|
475 |
self._fn = fn |
|
476 |
self._defer = None |
|
477 |
|
|
478 |
def CanShutdown(self): |
|
479 |
"""Checks whether mainloop can be stopped. |
|
480 |
|
|
481 |
@rtype: bool |
|
482 |
|
|
483 |
""" |
|
484 |
if self._defer and self._defer.Remaining() > 0: |
|
485 |
# A deferred check has already been scheduled |
|
486 |
return False |
|
487 |
|
|
488 |
# Ask mainloop driver whether we can stop or should check again |
|
489 |
timeout = self._fn() |
|
490 |
|
|
491 |
if timeout is None: |
|
492 |
# Yes, can stop mainloop |
|
493 |
return True |
|
494 |
|
|
495 |
# Schedule another check in the future |
|
496 |
self._defer = utils.RunningTimeout(timeout, True) |
|
497 |
|
|
498 |
return False |
|
499 |
|
|
500 |
|
|
460 | 501 |
class Mainloop(object): |
461 | 502 |
"""Generic mainloop for daemons |
462 | 503 |
|
... | ... | |
464 | 505 |
timed events |
465 | 506 |
|
466 | 507 |
""" |
508 |
_SHUTDOWN_TIMEOUT_PRIORITY = -(sys.maxint - 1) |
|
509 |
|
|
467 | 510 |
def __init__(self): |
468 | 511 |
"""Constructs a new Mainloop instance. |
469 | 512 |
|
... | ... | |
477 | 520 |
@utils.SignalHandled([signal.SIGCHLD]) |
478 | 521 |
@utils.SignalHandled([signal.SIGTERM]) |
479 | 522 |
@utils.SignalHandled([signal.SIGINT]) |
480 |
def Run(self, signal_handlers=None): |
|
523 |
def Run(self, shutdown_wait_fn=None, signal_handlers=None):
|
|
481 | 524 |
"""Runs the mainloop. |
482 | 525 |
|
526 |
@type shutdown_wait_fn: callable |
|
527 |
@param shutdown_wait_fn: Function to check whether loop can be terminated; |
|
528 |
B{important}: function must be idempotent and must return either None |
|
529 |
for shutting down or a timeout for another call |
|
483 | 530 |
@type signal_handlers: dict |
484 | 531 |
@param signal_handlers: signal->L{utils.SignalHandler} passed by decorator |
485 | 532 |
|
... | ... | |
491 | 538 |
# Counter for received signals |
492 | 539 |
shutdown_signals = 0 |
493 | 540 |
|
541 |
# Logic to wait for shutdown |
|
542 |
shutdown_waiter = None |
|
543 |
|
|
494 | 544 |
# Start actual main loop |
495 |
while shutdown_signals < 1: |
|
496 |
if not self.scheduler.empty(): |
|
545 |
while True: |
|
546 |
if shutdown_signals == 1 and shutdown_wait_fn is not None: |
|
547 |
if shutdown_waiter is None: |
|
548 |
shutdown_waiter = _ShutdownCheck(shutdown_wait_fn) |
|
549 |
|
|
550 |
# Let mainloop driver decide if we can already abort |
|
551 |
if shutdown_waiter.CanShutdown(): |
|
552 |
break |
|
553 |
|
|
554 |
# Re-evaluate in a second |
|
555 |
timeout = 1.0 |
|
556 |
|
|
557 |
elif shutdown_signals >= 1: |
|
558 |
# Abort loop if more than one signal has been sent or no callback has |
|
559 |
# been given |
|
560 |
break |
|
561 |
|
|
562 |
else: |
|
563 |
# Wait forever on I/O events |
|
564 |
timeout = None |
|
565 |
|
|
566 |
if self.scheduler.empty(): |
|
567 |
asyncore.loop(count=1, timeout=timeout, use_poll=True) |
|
568 |
else: |
|
497 | 569 |
try: |
498 |
self.scheduler.run() |
|
570 |
self.scheduler.run(max_delay=timeout)
|
|
499 | 571 |
except SchedulerBreakout: |
500 | 572 |
pass |
501 |
else: |
|
502 |
asyncore.loop(count=1, use_poll=True) |
|
503 | 573 |
|
504 | 574 |
# Check whether a signal was raised |
505 | 575 |
for (sig, handler) in signal_handlers.items(): |
Also available in: Unified diff