File tree Expand file tree Collapse file tree 3 files changed +49
-1
lines changed Expand file tree Collapse file tree 3 files changed +49
-1
lines changed Original file line number Diff line number Diff 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
Original file line number Diff line number Diff line change 33import collections
44import errno
55import io
6+ import multiprocessing
67import os
78import pathlib
89import signal
1213import tempfile
1314import threading
1415import unittest
15- import warnings
1616from unittest import mock
1717
1818if 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+
15591590class PolicyTests (unittest .TestCase ):
15601591
15611592 def create_policy (self ):
Original file line number Diff line number Diff line change 1+ Fix Policy.get_event_loop() to detect fork and return a new loop.
2+
3+ Original patch by Dan O'Reilly.
You can’t perform that action at this time.
0 commit comments