Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0c1fbc1

Browse files
GH-66285: fix forking inasyncio (#99539)
`asyncio` now does not shares event loop and signal wakeupfd in forked processes.
1 parent9dc0836 commit0c1fbc1

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

‎Lib/asyncio/events.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
importsubprocess
1818
importsys
1919
importthreading
20+
importsignal
2021

2122
from .importformat_helpers
2223

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

666667
def__init__(self):
667668
self._local=self._Local()
669+
ifhasattr(os,'fork'):
670+
defon_fork():
671+
# Reset the loop and wakeupfd in the forked child process.
672+
self._local=self._Local()
673+
signal.set_wakeup_fd(-1)
674+
675+
os.register_at_fork(after_in_child=on_fork)
676+
668677

669678
defget_event_loop(self):
670679
"""Get the event loop for the current context.

‎Lib/test/test_asyncio/test_unix_events.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
importsys
1212
importthreading
1313
importunittest
14+
importtime
1415
fromunittestimportmock
1516
importwarnings
17+
importmultiprocessing
1618
fromtest.supportimportos_helper
1719
fromtest.supportimportsocket_helper
20+
fromtest.supportimportwait_process
1821

1922
ifsys.platform=='win32':
2023
raiseunittest.SkipTest('UNIX only')
@@ -1867,5 +1870,97 @@ async def runner():
18671870
wsock.close()
18681871

18691872

1873+
@unittest.skipUnless(hasattr(os,'fork'),'requires os.fork()')
1874+
classTestFork(unittest.IsolatedAsyncioTestCase):
1875+
1876+
asyncdeftest_fork_not_share_event_loop(self):
1877+
# The forked process should not share the event loop with the parent
1878+
loop=asyncio.get_running_loop()
1879+
r,w=os.pipe()
1880+
self.addCleanup(os.close,r)
1881+
self.addCleanup(os.close,w)
1882+
pid=os.fork()
1883+
ifpid==0:
1884+
# child
1885+
try:
1886+
loop=asyncio.get_event_loop_policy().get_event_loop()
1887+
os.write(w,str(id(loop)).encode())
1888+
finally:
1889+
os._exit(0)
1890+
else:
1891+
# parent
1892+
child_loop=int(os.read(r,100).decode())
1893+
self.assertNotEqual(child_loop,id(loop))
1894+
wait_process(pid,exitcode=0)
1895+
1896+
deftest_fork_signal_handling(self):
1897+
# Sending signal to the forked process should not affect the parent
1898+
# process
1899+
ctx=multiprocessing.get_context('fork')
1900+
manager=ctx.Manager()
1901+
self.addCleanup(manager.shutdown)
1902+
child_started=manager.Event()
1903+
child_handled=manager.Event()
1904+
parent_handled=manager.Event()
1905+
1906+
defchild_main():
1907+
signal.signal(signal.SIGTERM,lambda*args:child_handled.set())
1908+
child_started.set()
1909+
time.sleep(1)
1910+
1911+
asyncdefmain():
1912+
loop=asyncio.get_running_loop()
1913+
loop.add_signal_handler(signal.SIGTERM,lambda*args:parent_handled.set())
1914+
1915+
process=ctx.Process(target=child_main)
1916+
process.start()
1917+
child_started.wait()
1918+
os.kill(process.pid,signal.SIGTERM)
1919+
process.join()
1920+
1921+
asyncdeffunc():
1922+
awaitasyncio.sleep(0.1)
1923+
return42
1924+
1925+
# Test parent's loop is still functional
1926+
self.assertEqual(awaitasyncio.create_task(func()),42)
1927+
1928+
asyncio.run(main())
1929+
1930+
self.assertFalse(parent_handled.is_set())
1931+
self.assertTrue(child_handled.is_set())
1932+
1933+
deftest_fork_asyncio_run(self):
1934+
ctx=multiprocessing.get_context('fork')
1935+
manager=ctx.Manager()
1936+
self.addCleanup(manager.shutdown)
1937+
result=manager.Value('i',0)
1938+
1939+
asyncdefchild_main():
1940+
awaitasyncio.sleep(0.1)
1941+
result.value=42
1942+
1943+
process=ctx.Process(target=lambda:asyncio.run(child_main()))
1944+
process.start()
1945+
process.join()
1946+
1947+
self.assertEqual(result.value,42)
1948+
1949+
deftest_fork_asyncio_subprocess(self):
1950+
ctx=multiprocessing.get_context('fork')
1951+
manager=ctx.Manager()
1952+
self.addCleanup(manager.shutdown)
1953+
result=manager.Value('i',1)
1954+
1955+
asyncdefchild_main():
1956+
proc=awaitasyncio.create_subprocess_exec(sys.executable,'-c','pass')
1957+
result.value=awaitproc.wait()
1958+
1959+
process=ctx.Process(target=lambda:asyncio.run(child_main()))
1960+
process.start()
1961+
process.join()
1962+
1963+
self.assertEqual(result.value,0)
1964+
18701965
if__name__=='__main__':
18711966
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix:mod:`asyncio` to not share event loop and signal wakeupfd in forked processes. Patch by Kumar Aditya.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp