简体   繁体   中英

Implementing signal handling on a sleep loop

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM