File tree 3 files changed +49
-1
lines changed
3 files changed +49
-1
lines changed Original file line number Diff line number Diff line change @@ -583,16 +583,29 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
583
583
584
584
class _Local (threading .local ):
585
585
_loop = None
586
+ _pid = None
586
587
_set_called = False
587
588
589
+ def __init__ (self ):
590
+ super ().__init__ ()
591
+ self ._pid = os .getpid ()
592
+
588
593
def __init__ (self ):
589
594
self ._local = self ._Local ()
590
595
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
+
591
602
def get_event_loop (self ):
592
603
"""Get the event loop.
593
604
594
605
This may be None or an instance of EventLoop.
595
606
"""
607
+ self ._check_pid ()
608
+
596
609
if (self ._local ._loop is None and
597
610
not self ._local ._set_called and
598
611
isinstance (threading .current_thread (), threading ._MainThread )):
@@ -604,6 +617,7 @@ def get_event_loop(self):
604
617
605
618
def set_event_loop (self , loop ):
606
619
"""Set the event loop."""
620
+ self ._check_pid ()
607
621
self ._local ._set_called = True
608
622
assert loop is None or isinstance (loop , AbstractEventLoop )
609
623
self ._local ._loop = loop
Original file line number Diff line number Diff line change 3
3
import collections
4
4
import errno
5
5
import io
6
+ import multiprocessing
6
7
import os
7
8
import pathlib
8
9
import signal
12
13
import tempfile
13
14
import threading
14
15
import unittest
15
- import warnings
16
16
from unittest import mock
17
17
18
18
if sys .platform == 'win32' :
@@ -1556,6 +1556,37 @@ def create_watcher(self):
1556
1556
return asyncio .FastChildWatcher ()
1557
1557
1558
1558
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
+
1559
1590
class PolicyTests (unittest .TestCase ):
1560
1591
1561
1592
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