Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e8dfb2c

Browse files
committedMay 29, 2018
bpo-22087: Fix Policy.get_event_loop() to detect fork (GH-7208)
Original patch by Dan O'Reilly.
1 parent 51bf38f commit e8dfb2c

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed
 

‎Lib/asyncio/events.py

+14
Original file line numberDiff line numberDiff line change
@@ -583,16 +583,29 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
583583

584584
class _Local(threading.local):
585585
_loop = None
586+
_pid = None
586587
_set_called = False
587588

589+
def __init__(self):
590+
super().__init__()
591+
self._pid = os.getpid()
592+
588593
def __init__(self):
589594
self._local = self._Local()
590595

596+
def _check_pid(self):
597+
if self._local._pid != os.getpid():
598+
# If we detect we're in a child process forked by multiprocessing,
599+
# we reset self._local so that we'll get a new event loop.
600+
self._local = self._Local()
601+
591602
def get_event_loop(self):
592603
"""Get the event loop.
593604
594605
This may be None or an instance of EventLoop.
595606
"""
607+
self._check_pid()
608+
596609
if (self._local._loop is None and
597610
not self._local._set_called and
598611
isinstance(threading.current_thread(), threading._MainThread)):
@@ -604,6 +617,7 @@ def get_event_loop(self):
604617

605618
def set_event_loop(self, loop):
606619
"""Set the event loop."""
620+
self._check_pid()
607621
self._local._set_called = True
608622
assert loop is None or isinstance(loop, AbstractEventLoop)
609623
self._local._loop = loop

‎Lib/test/test_asyncio/test_unix_events.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import collections
44
import errno
55
import io
6+
import multiprocessing
67
import os
78
import pathlib
89
import signal
@@ -12,7 +13,6 @@
1213
import tempfile
1314
import threading
1415
import unittest
15-
import warnings
1616
from unittest import mock
1717

1818
if sys.platform == 'win32':
@@ -1556,6 +1556,37 @@ def create_watcher(self):
15561556
return asyncio.FastChildWatcher()
15571557

15581558

1559+
class ForkedProcessTests(unittest.TestCase):
1560+
def setUp(self):
1561+
self.parent_loop = asyncio.SelectorEventLoop()
1562+
asyncio.set_event_loop(self.parent_loop)
1563+
self.ctx = multiprocessing.get_context("fork")
1564+
1565+
def tearDown(self):
1566+
self.parent_loop.close()
1567+
1568+
def _check_loops_not_equal(self, old_loop):
1569+
loop = asyncio.get_event_loop()
1570+
if loop is old_loop:
1571+
raise RuntimeError("Child process inherited parent's event loop")
1572+
1573+
try:
1574+
val = loop.run_until_complete(asyncio.sleep(0.05, result=42))
1575+
if val != 42:
1576+
raise RuntimeError("new event loop does not work")
1577+
finally:
1578+
loop.close()
1579+
1580+
sys.exit(loop is old_loop)
1581+
1582+
def test_new_loop_in_child(self):
1583+
p = self.ctx.Process(target=self._check_loops_not_equal,
1584+
args=(self.parent_loop,))
1585+
p.start()
1586+
p.join()
1587+
self.assertEqual(p.exitcode, 0)
1588+
1589+
15591590
class PolicyTests(unittest.TestCase):
15601591

15611592
def create_policy(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix Policy.get_event_loop() to detect fork and return a new loop.
2+
3+
Original patch by Dan O'Reilly.

0 commit comments

Comments
 (0)
Please sign in to comment.