Skip to content

Commit aefab6b

Browse files
fix forking
1 parent f13f466 commit aefab6b

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

Lib/asyncio/events.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import subprocess
1818
import sys
1919
import threading
20+
import signal
2021

2122
from . import format_helpers
2223

@@ -665,6 +666,13 @@ class _Local(threading.local):
665666

666667
def __init__(self):
667668
self._local = self._Local()
669+
if hasattr(os, 'fork'):
670+
def on_fork():
671+
self._local = self._Local()
672+
signal.set_wakeup_fd(-1)
673+
674+
os.register_at_fork(after_in_child=on_fork)
675+
668676

669677
def get_event_loop(self):
670678
"""Get the event loop for the current context.

Lib/test/test_asyncio/test_unix_events.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
import sys
1212
import threading
1313
import unittest
14+
import time
1415
from unittest import mock
1516
import warnings
17+
import multiprocessing
1618
from test.support import os_helper
1719
from test.support import socket_helper
20+
from test.support import wait_process
1821

1922
if sys.platform == 'win32':
2023
raise unittest.SkipTest('UNIX only')
@@ -1867,5 +1870,61 @@ async def runner():
18671870
wsock.close()
18681871

18691872

1873+
@unittest.skipUnless(hasattr(os, 'fork'), 'requires os.fork()')
1874+
class TestFork(unittest.IsolatedAsyncioTestCase):
1875+
1876+
async def test_fork(self):
1877+
loop = asyncio.get_running_loop()
1878+
r, w = os.pipe()
1879+
self.addCleanup(os.close, r)
1880+
self.addCleanup(os.close, w)
1881+
pid = os.fork()
1882+
if pid == 0:
1883+
# child
1884+
try:
1885+
loop = asyncio.get_event_loop_policy().get_event_loop()
1886+
os.write(w, str(id(loop)).encode())
1887+
finally:
1888+
os._exit(0)
1889+
else:
1890+
# parent
1891+
child_loop = int(os.read(r, 100).decode())
1892+
self.assertNotEqual(child_loop, id(loop))
1893+
wait_process(pid, exitcode=0)
1894+
1895+
def test_fork_signal_handling(self):
1896+
multiprocessing.set_start_method('fork')
1897+
manager = multiprocessing.Manager()
1898+
self.addCleanup(manager.shutdown)
1899+
child_started = manager.Event()
1900+
child_handler_called = manager.Event()
1901+
parent_handler_called = manager.Event()
1902+
1903+
def parent_handler(*args):
1904+
parent_handler_called.set()
1905+
1906+
def child_handler(*args):
1907+
child_handler_called.set()
1908+
1909+
def child_main():
1910+
signal.signal(signal.SIGTERM, child_handler)
1911+
child_started.set()
1912+
time.sleep(1)
1913+
1914+
async def main():
1915+
loop = asyncio.get_running_loop()
1916+
loop.add_signal_handler(signal.SIGTERM, parent_handler)
1917+
1918+
process = multiprocessing.Process(target=child_main)
1919+
process.start()
1920+
child_started.wait()
1921+
os.kill(process.pid, signal.SIGTERM)
1922+
process.join()
1923+
1924+
asyncio.run(main())
1925+
1926+
self.assertFalse(parent_handler_called.is_set())
1927+
self.assertTrue(child_handler_called.is_set())
1928+
18701929
if __name__ == '__main__':
18711930
unittest.main()

0 commit comments

Comments
 (0)