Max Tet
Max Tet

Reputation: 755

Indirectly stopping a python asyncio event loop through SIGTERM has no effect

The following minimal program reproduces the problem.

import asyncio
import signal

class A:
    def __init__(self):
        self._event_loop = asyncio.new_event_loop()

    def run(self):
        print('starting event loop')
        self._event_loop.run_forever()
        print('event loop has stopped')

    def stop(self):
        print('stopping event loop')
        self._event_loop.stop()


if __name__ == '__main__':
    a = A()

    def handle_term(*args):
        a.stop()

    signal.signal(signal.SIGTERM, handle_term)
    a.run()

If you run the program and send a SIGTERM to the process, the print statement in line 16 (stopping event loop) is called but the programm does not terminate and the print statement in line 13 (event loop has stopped) is never called. So it seems that the event loop is never stopped and self._event_loop.run_forever() blocks indefinitely.

Why is this?

Note: A modified version of the program, where a.stop() is not called by a signal handler but by a seperate thread with a delay, works as expected. How can it make a difference how a.stop() is called?

Upvotes: 7

Views: 5029

Answers (1)

Udi
Udi

Reputation: 30534

Instead of signal.signal() use loop.add_signal_handler():

import asyncio
import signal

import os


class A:
    def __init__(self):
        self.loop = asyncio.new_event_loop()
        self.loop.add_signal_handler(signal.SIGTERM, self.stop)

    def stop(self):
        print('stopping')
        self.loop.stop()

    def run(self, close=True):
        print('starting loop')
        try:
            self.loop.run_forever()
            print('loop stopped')
        finally:
            if close:
                self.loop.close()


if __name__ == '__main__':
    print("to stop run:\nkill -TERM {}".format(os.getpid()))
    a = A()
    a.run()

Upvotes: 7

Related Questions