New submission from Selim Belhaouane <selim.belhaou...@gmail.com>:

Cannot clear signal handler set with loop.add_signal_handler in forked process 
with signal.signal

# Context

I'm running an async web server with uvicorn[1] and have background processes 
(using multiprocessing) doing CPU-bound work. uvicorn install signal handlers 
with loop.add*signal_handler, if available[2]. It also implements the common 
"attempt graceful shutdown on the first signal, and forcefully shutdown on the 
second (or more) signal" pattern. The problem I noticed was: when \_forking* at 
least one process, the server never gracefully shuts down, **even if I install 
new signal handlers in the subprocesses**.

# Problem

Signal handlers installed with loop.add*signal_handler cannot be cleared in 
forked processes with `signal.signal` (see Experiment 1), \_unless* using 
`signal.SIG_DFL` or `signal.SIG_IGN` in the forked processes (see Experiment 3).

For the record:

- When installing signal handlers in the parent process with signal.signal, 
this is not a problem (see Experiment 2).
- When using multiprocessing with the "spawn" method, this is not a problem. 
Unsurprising, since signal handlers are not inherited from the parent process 
when using "spawn" (see Experiment 4).
- When install signal handlers in the child proceess with 
loop.add_signal_handler, this is not a problem (see Experiment 5).

# Experiments

You'll find a minimal `exp.py` file in the attached archive. There's a few 
tweakable parameters at the top. I just run this with `python3.X exp.py`, wait 
a second or two, and hit Ctrl+C.
`results.txt` shows the results of a few experiments with different set of 
parameters.
Note that the point at which I hit Ctrl+C is indicated by the ♥ (heart) symbol, 
due to some terminal weirdness, although this is quite useful in this case!
The subsections below detail what happens when Ctrl+C is hit.
You guys probably know this already, but Ctrl+C basically sends SIGINT to all 
processes in the process tree.

## Experiment 1

- Installs a signal handler in the parent process with loop.add_signal_handler
- Starts 3 subprocesses with "fork"
- The subprocesses install a new signal handler with signal.signal

Outcome: both the parent handler and child handlers get called (3 calls to 
handle_sig_worker, 4 calls to handle_sig_main)

**Expected**: I would expect a single call to handle_sig_main, and 3 calls to 
handle_sig_worker, as in Experiment 2 (which uses signal.signal instead of 
loop.add_signal_handler) or Experiment 4 (which uses "spawn" instead of "fork") 
or Experiment 5 ()

## Experiment 2

As Experiment 1, but signal handlers are installed in the parent process with 
signal.signal

Outcome: the parent handler gets called once, and the child handlers get called 
3 times

## Experiment 3

Same as Experiment 1, but this time using `signal.SIG_DFL` as the callback in 
the child processes.

Outcome: the child processes immediately terminate, thanks to SIG_DFL, and the 
parent handler gets called only once!

## Experiment 4

Same as Experiment 1, but this time using "spawn" as the multiprocessing start 
method.

Outcome: same as Experiment 2: parent handlers gets called once, child handlers 
get called 3 times

## Experiment 5

Same as Experiment 1, but this time installing signal handlers in the child 
processes with loop.add_signal_handler.

Outcome: Same as Experiment 2.

# Environment

I was able to replicate the issue with python 3.7, 3.8, 3.9 and 3.10 (didn't 
even try 3.6) on Ubuntu 20.04. I obtained python via `apt`.

[1]: https://www.uvicorn.org/
[2]: 
https://github.com/encode/uvicorn/blob/61a6cabb4580e1c923df396eac264803f599412c/uvicorn/server.py#L281

----------
components: asyncio
files: python-async-signal-bug.zip
messages: 385928
nosy: asvetlov, selimb, yselivanov
priority: normal
severity: normal
status: open
title: Impossible to override signal handler set with add_signal_handler in 
forked process
type: behavior
versions: Python 3.10, Python 3.7, Python 3.8, Python 3.9
Added file: https://bugs.python.org/file49774/python-async-signal-bug.zip

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue43064>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to