Skip to content

Commit 7f89ead

Browse files
authored
Fix signal handler reentrancy deadlock (patroni#3413)
The problem existed only when Patroni is running in Docker container with PID=1
1 parent 2953276 commit 7f89ead

File tree

2 files changed

+12
-7
lines changed

2 files changed

+12
-7
lines changed

patroni/__main__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,12 @@ def passtochild(signo: int, stack_frame: Optional[FrameType]) -> None:
406406
if pid:
407407
os.kill(pid, signo)
408408

409+
import multiprocessing
410+
patroni = multiprocessing.Process(target=patroni_main, args=(args.configfile,))
411+
patroni.start()
412+
pid = patroni.pid
413+
414+
# Set up signal handlers after fork to prevent child from inheriting them
409415
if os.name != 'nt':
410416
signal.signal(signal.SIGCHLD, sigchld_handler)
411417
signal.signal(signal.SIGHUP, passtochild)
@@ -415,11 +421,6 @@ def passtochild(signo: int, stack_frame: Optional[FrameType]) -> None:
415421
signal.signal(signal.SIGINT, passtochild)
416422
signal.signal(signal.SIGABRT, passtochild)
417423
signal.signal(signal.SIGTERM, passtochild)
418-
419-
import multiprocessing
420-
patroni = multiprocessing.Process(target=patroni_main, args=(args.configfile,))
421-
patroni.start()
422-
pid = patroni.pid
423424
patroni.join()
424425

425426

tests/test_patroni.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,13 @@ def test_patroni_main(self, mock_process, mock_getpid):
138138
def mock_signal(signo, handler):
139139
handler(signo, None)
140140

141-
with patch('signal.signal', mock_signal):
142-
with patch('os.waitpid', Mock(side_effect=[(1, 0), (0, 0)])):
141+
with patch('signal.signal', mock_signal), patch('os.kill') as mock_kill:
142+
with patch('os.waitpid', Mock(side_effect=[(1, 0), (0, 0)])), \
143+
patch('patroni.__main__.logger') as mock_logger:
143144
_main()
145+
mock_kill.assert_called_with(mock_process.return_value.pid, signal.SIGTERM)
146+
if os.name != 'nt':
147+
mock_logger.info.assert_called_with('Reaped pid=%s, exit status=%s', 1, 0)
144148
with patch('os.waitpid', Mock(side_effect=OSError)):
145149
_main()
146150

0 commit comments

Comments
 (0)