Skip to content

Commit 90c0e81

Browse files
Fix the race condition
1 parent 6ab65c6 commit 90c0e81

1 file changed

Lines changed: 48 additions & 24 deletions

File tree

‎Lib/asyncio/unix_events.py‎

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,22 +1219,34 @@ class MultiLoopChildWatcher(AbstractChildWatcher):
12191219

12201220
def __init__(self):
12211221
self._callbacks = {}
1222-
self._saved_sighandler = None
1222+
self._saved_sighandler = {}
1223+
self._handler_added_loops = []
12231224

12241225
def is_active(self):
1225-
return self._saved_sighandler is not None
1226+
return self._handler_added_loops is not []
12261227

12271228
def close(self):
12281229
self._callbacks.clear()
1229-
if self._saved_sighandler is None:
1230+
if self._handler_added_loops is []:
12301231
return
12311232

1232-
handler = signal.getsignal(signal.SIGCHLD)
1233-
if handler != self._sig_chld:
1234-
logger.warning("SIGCHLD handler was changed by outside code")
1235-
else:
1236-
signal.signal(signal.SIGCHLD, self._saved_sighandler)
1237-
self._saved_sighandler = None
1233+
# handler = signal.getsignal(signal.SIGCHLD)
1234+
# if handler != self._sig_chld:
1235+
# logger.warning("SIGCHLD handler was changed by outside code")
1236+
# else:
1237+
# signal.signal(signal.SIGCHLD, self._saved_sighandler)
1238+
# self._saved_sighandler = None
1239+
1240+
if self._handler_added_loops is not [] and self._callbacks:
1241+
warnings.warn(
1242+
'Event Loops are being detached '
1243+
'from a child watcher with pending handlers',
1244+
RuntimeWarning)
1245+
1246+
if self._handler_added_loops is not []:
1247+
for loop in self._handler_added_loops:
1248+
loop.remove_signal_handler(signal.SIGCHLD)
1249+
self._handler_added_loops.remove(loop)
12381250

12391251
def __enter__(self):
12401252
return self
@@ -1244,6 +1256,9 @@ def __exit__(self, exc_type, exc_val, exc_tb):
12441256

12451257
def add_child_handler(self, pid, callback, *args):
12461258
loop = events.get_running_loop()
1259+
if loop not in self._handler_added_loops:
1260+
self.attach_loop(loop)
1261+
12471262
self._callbacks[pid] = (loop, callback, args)
12481263

12491264
# Prevent a race condition in case the child is already terminated.
@@ -1257,21 +1272,30 @@ def remove_child_handler(self, pid):
12571272
return False
12581273

12591274
def attach_loop(self, loop):
1260-
# Don't save the loop but initialize itself if called first time
1261-
# The reason to do it here is that attach_loop() is called from
1262-
# unix policy only for the main thread.
1263-
# Main thread is required for subscription on SIGCHLD signal
1264-
if self._saved_sighandler is not None:
1265-
return
1266-
1267-
self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld)
1268-
if self._saved_sighandler is None:
1269-
logger.warning("Previous SIGCHLD handler was set by non-Python code, "
1270-
"restore to default handler on watcher close.")
1271-
self._saved_sighandler = signal.SIG_DFL
1275+
# # Don't save the loop but initialize itself if called first time
1276+
# # The reason to do it here is that attach_loop() is called from
1277+
# # unix policy only for the main thread.
1278+
# # Main thread is required for subscription on SIGCHLD signal
1279+
# if self._saved_sighandler is not None:
1280+
# return
1281+
1282+
# self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld)
1283+
# if self._saved_sighandler is None:
1284+
# logger.warning("Previous SIGCHLD handler was set by non-Python code, "
1285+
# "restore to default handler on watcher close.")
1286+
# self._saved_sighandler = signal.SIG_DFL
1287+
1288+
# # Set SA_RESTART to limit EINTR occurrences.
1289+
# signal.siginterrupt(signal.SIGCHLD, False)
1290+
assert loop is None or isinstance(loop, events.AbstractEventLoop)
1291+
1292+
if loop is not None:
1293+
loop.add_signal_handler(signal.SIGCHLD, self._sig_chld)
12721294

1273-
# Set SA_RESTART to limit EINTR occurrences.
1274-
signal.siginterrupt(signal.SIGCHLD, False)
1295+
# Prevent a race condition in case a child terminated
1296+
# during the switch.
1297+
self._do_waitpid_all()
1298+
self._handler_added_loops.append(loop)
12751299

12761300
def _do_waitpid_all(self):
12771301
for pid in list(self._callbacks):
@@ -1314,7 +1338,7 @@ def _do_waitpid(self, expected_pid):
13141338
expected_pid, returncode)
13151339
loop.call_soon_threadsafe(callback, pid, returncode, *args)
13161340

1317-
def _sig_chld(self, signum, frame):
1341+
def _sig_chld(self):
13181342
try:
13191343
self._do_waitpid_all()
13201344
except (SystemExit, KeyboardInterrupt):

0 commit comments

Comments
 (0)