sanic icon indicating copy to clipboard operation
sanic copied to clipboard

Not properly handle signal to stop worker sub-process in debug mode

Open cfhamlet opened this issue 5 years ago • 0 comments

Describe the bug In the debug mode, if you do not change your source code to trigger auto reload, the stop signals(SIGTERM, SIGINT) send to the main process will not work properly to stop the worker sub-process. As the following code, run it and stop(Ctrl-C), the on_shutdown method will not be called.

# v18.12.0
from sanic import Sanic

app = Sanic(__name__)

@app.listener('after_server_stop')
def on_shutdown(app, loop):
    print('I am done!')

app.run(debug=True)

I think the problem maybe caused by the following code in reloader_helpers.py:

def watchdog(sleep_interval):
    mtimes = {}
    worker_process = restart_with_reloader()
    signal.signal(
        signal.SIGTERM, lambda *args: kill_program_completly(worker_process)
    )
    signal.signal(
        signal.SIGINT, lambda *args: kill_program_completly(worker_process)
    )

Because of creating worker_process before register signal hander(kill_program_completly), the woker_process still use the default signal handler and do not kill sub-process woker.

The following snippet is a immature solution, just for reference:

def kill_program_completly(proc):
    if not proc:
        kill_process_children(os.getpid())
        os._exit(0)
    proc = proc.pop()
    kill_process_children(proc.pid)
    proc.terminate()
    os._exit(0)


def watchdog(sleep_interval):
    mtimes = {}
    worker_process = []
    signal.signal(
        signal.SIGTERM, lambda *args: kill_program_completly(worker_process)
    )
    signal.signal(
        signal.SIGINT, lambda *args: kill_program_completly(worker_process)
    )

    worker_process.append(restart_with_reloader())
    while True:
        for filename in _iter_module_files():
            try:
                mtime = os.stat(filename).st_mtime
            except OSError:
                continue

            old_time = mtimes.get(filename)
            if old_time is None:
                mtimes[filename] = mtime
                continue
            elif mtime > old_time:
                wp = worker_process.pop()
                kill_process_children(wp.pid)
                wp.terminate()
                worker_process.append(restart_with_reloader())
                mtimes[filename] = mtime
                break

        sleep(sleep_interval)

cfhamlet avatar Jan 15 '19 15:01 cfhamlet