File tree Expand file tree Collapse file tree 3 files changed +43
-5
lines changed Expand file tree Collapse file tree 3 files changed +43
-5
lines changed Original file line number Diff line number Diff line change @@ -220,7 +220,7 @@ def init_poller(self):
220
220
# PID 1 (init) is special and will never go away,
221
221
# only be reassigned.
222
222
# Parent polling doesn't work if ppid == 1 to start with.
223
- self .poller = ParentPollerUnix ()
223
+ self .poller = ParentPollerUnix (parent_pid = self . parent_handle )
224
224
225
225
def _try_bind_socket (self , s , port ):
226
226
iface = f"{ self .transport } ://{ self .ip } "
Original file line number Diff line number Diff line change @@ -22,19 +22,41 @@ class ParentPollerUnix(Thread):
22
22
when the parent process no longer exists.
23
23
"""
24
24
25
- def __init__ (self ):
26
- """Initialize the poller."""
25
+ def __init__ (self , parent_pid = 0 ):
26
+ """Initialize the poller.
27
+
28
+ Parameters
29
+ ----------
30
+ parent_handle : int, optional
31
+ If provided, the program will terminate immediately when
32
+ process parent is no longer this original parent.
33
+ """
27
34
super ().__init__ ()
35
+ self .parent_pid = parent_pid
28
36
self .daemon = True
29
37
30
38
def run (self ):
31
39
"""Run the poller."""
32
40
# We cannot use os.waitpid because it works only for child processes.
33
41
from errno import EINTR
34
42
43
+ # before start, check if the passed-in parent pid is valid
44
+ original_ppid = os .getppid ()
45
+ if original_ppid != self .parent_pid :
46
+ self .parent_pid = 0
47
+
48
+ get_logger ().debug (
49
+ "%s: poll for parent change with original parent pid=%d" ,
50
+ type (self ).__name__ ,
51
+ self .parent_pid ,
52
+ )
53
+
35
54
while True :
36
55
try :
37
- if os .getppid () == 1 :
56
+ ppid = os .getppid ()
57
+ parent_is_init = not self .parent_pid and ppid == 1
58
+ parent_has_changed = self .parent_pid and ppid != self .parent_pid
59
+ if parent_is_init or parent_has_changed :
38
60
get_logger ().warning ("Parent appears to have exited, shutting down." )
39
61
os ._exit (1 )
40
62
time .sleep (1.0 )
Original file line number Diff line number Diff line change 9
9
10
10
11
11
@pytest .mark .skipif (os .name == "nt" , reason = "only works on posix" )
12
- def test_parent_poller_unix ():
12
+ def test_parent_poller_unix_to_pid1 ():
13
13
poller = ParentPollerUnix ()
14
14
with mock .patch ("os.getppid" , lambda : 1 ): # noqa: PT008
15
15
@@ -27,6 +27,22 @@ def mock_getppid():
27
27
poller .run ()
28
28
29
29
30
+ @pytest .mark .skipif (os .name == "nt" , reason = "only works on posix" )
31
+ def test_parent_poller_unix_reparent_not_pid1 ():
32
+ parent_pid = 221
33
+ parent_pids = iter ([parent_pid , parent_pid - 1 ])
34
+
35
+ poller = ParentPollerUnix (parent_pid = parent_pid )
36
+
37
+ with mock .patch ("os.getppid" , lambda : next (parent_pids )): # noqa: PT008
38
+
39
+ def exit_mock (* args ):
40
+ sys .exit (1 )
41
+
42
+ with mock .patch ("os._exit" , exit_mock ), pytest .raises (SystemExit ):
43
+ poller .run ()
44
+
45
+
30
46
@pytest .mark .skipif (os .name != "nt" , reason = "only works on windows" )
31
47
def test_parent_poller_windows ():
32
48
poller = ParentPollerWindows (interrupt_handle = 1 )
You can’t perform that action at this time.
0 commit comments