One of the modules in an app I'm working on is intended to be used as a long-running process on Linux, and I would like it to gracefully handle SIGTERM, SIGHUP and possibly other signals. The core part of the program is in fact a loop which periodically runs a function (which in turn wakes up another thread, but this is less important). It looks more or less like this:
while True:
try:
do_something()
sleep(60)
except KeyboardInterrupt:
break
cleanup_and_exit()
What I'd like to add now is to catch SIGTERM and exit the loop, the same way a KeyboardInterrupt
exception would.
One thought I have is to add a flag which will be set to True by the signal handler function, and replace the sleep(60) with sleep(0.1) or whatever, with a counter that counts seconds:
_exit_flag = False
while not _exit_flag:
try:
for _ in xrange(600):
if _exit_flag: break
do_something()
sleep(0.1)
except KeyboardInterrupt:
break
cleanup_and_exit()
and somewhere else:
def signal_handler(sig, frame):
_exit_flag = True
But I'm not sure this is the best / most efficient way to do it.
Rather than using a sentinel in the main loop and thus having to wake more frequently than you'd really like to check for it, why not push the cleanup in to the handler? Something like:
class BlockingAction(object):
def __new__(cls, action):
if isinstance(action, BlockingAction):
return action
else:
new_action = super(BlockingAction, cls).__new__(cls)
new_action.action = action
new_action.active = False
return new_action
def __call__(self, *args, **kwargs):
self.active = True
result = self.action(*args, **kwargs)
self.active = False
return result
class SignalHandler(object):
def __new__(cls, sig, action):
if isinstance(action, SignalHandler):
handler = action
else:
handler = super(SignalHandler, cls).__new__(cls)
handler.action = action
handler.blocking_actions = []
signal.signal(sig, handler)
return handler
def __call__(self, signum, frame):
while any(a.active for a in self.blocking_actions):
time.sleep(.01)
return self.action()
def blocks_on(self, action):
blocking_action = BlockingAction(action)
self.blocking_actions.append(blocking_action)
return blocking_action
def handles(signal):
def get_handler(action):
return SignalHandler(signal, action)
return get_handler
@handles(signal.SIGTERM)
@handles(signal.SIGHUP)
@handles(signal.SIGINT)
def cleanup_and_exit():
# Note that this assumes that this method actually exits the program explicitly
# If it does not, you'll need some form of sentinel for the while loop
pass
@cleanup_and_exit.blocks_on
def do_something():
pass
while True:
do_something()
time.sleep(60)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.