mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +00:00
gh-90549: Fix leak of global named resources using multiprocessing spawn (GH-30617)
Co-authored-by: XD Trol <milestonejxd@gmail.com>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
(cherry picked from commit 30610d2837
)
Co-authored-by: Leo Trol <milestone.jxd@gmail.com>
This commit is contained in:
parent
8d8251a9b1
commit
2ad51c636a
4 changed files with 54 additions and 2 deletions
|
@ -223,6 +223,10 @@ class Process(process.BaseProcess):
|
||||||
def _Popen(process_obj):
|
def _Popen(process_obj):
|
||||||
return _default_context.get_context().Process._Popen(process_obj)
|
return _default_context.get_context().Process._Popen(process_obj)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _after_fork():
|
||||||
|
return _default_context.get_context().Process._after_fork()
|
||||||
|
|
||||||
class DefaultContext(BaseContext):
|
class DefaultContext(BaseContext):
|
||||||
Process = Process
|
Process = Process
|
||||||
|
|
||||||
|
@ -283,6 +287,11 @@ if sys.platform != 'win32':
|
||||||
from .popen_spawn_posix import Popen
|
from .popen_spawn_posix import Popen
|
||||||
return Popen(process_obj)
|
return Popen(process_obj)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _after_fork():
|
||||||
|
# process is spawned, nothing to do
|
||||||
|
pass
|
||||||
|
|
||||||
class ForkServerProcess(process.BaseProcess):
|
class ForkServerProcess(process.BaseProcess):
|
||||||
_start_method = 'forkserver'
|
_start_method = 'forkserver'
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -326,6 +335,11 @@ else:
|
||||||
from .popen_spawn_win32 import Popen
|
from .popen_spawn_win32 import Popen
|
||||||
return Popen(process_obj)
|
return Popen(process_obj)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _after_fork():
|
||||||
|
# process is spawned, nothing to do
|
||||||
|
pass
|
||||||
|
|
||||||
class SpawnContext(BaseContext):
|
class SpawnContext(BaseContext):
|
||||||
_name = 'spawn'
|
_name = 'spawn'
|
||||||
Process = SpawnProcess
|
Process = SpawnProcess
|
||||||
|
|
|
@ -304,8 +304,7 @@ class BaseProcess(object):
|
||||||
if threading._HAVE_THREAD_NATIVE_ID:
|
if threading._HAVE_THREAD_NATIVE_ID:
|
||||||
threading.main_thread()._set_native_id()
|
threading.main_thread()._set_native_id()
|
||||||
try:
|
try:
|
||||||
util._finalizer_registry.clear()
|
self._after_fork()
|
||||||
util._run_after_forkers()
|
|
||||||
finally:
|
finally:
|
||||||
# delay finalization of the old process object until after
|
# delay finalization of the old process object until after
|
||||||
# _run_after_forkers() is executed
|
# _run_after_forkers() is executed
|
||||||
|
@ -336,6 +335,13 @@ class BaseProcess(object):
|
||||||
|
|
||||||
return exitcode
|
return exitcode
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _after_fork():
|
||||||
|
from . import util
|
||||||
|
util._finalizer_registry.clear()
|
||||||
|
util._run_after_forkers()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# We subclass bytes to avoid accidental transmission of auth keys over network
|
# We subclass bytes to avoid accidental transmission of auth keys over network
|
||||||
#
|
#
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
import queue as pyqueue
|
import queue as pyqueue
|
||||||
|
import textwrap
|
||||||
import time
|
import time
|
||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -5699,6 +5700,35 @@ class TestSyncManagerTypes(unittest.TestCase):
|
||||||
self.run_worker(self._test_namespace, o)
|
self.run_worker(self._test_namespace, o)
|
||||||
|
|
||||||
|
|
||||||
|
class TestNamedResource(unittest.TestCase):
|
||||||
|
def test_global_named_resource_spawn(self):
|
||||||
|
#
|
||||||
|
# gh-90549: Check that global named resources in main module
|
||||||
|
# will not leak by a subprocess, in spawn context.
|
||||||
|
#
|
||||||
|
testfn = os_helper.TESTFN
|
||||||
|
self.addCleanup(os_helper.unlink, testfn)
|
||||||
|
with open(testfn, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(textwrap.dedent('''\
|
||||||
|
import multiprocessing as mp
|
||||||
|
|
||||||
|
ctx = mp.get_context('spawn')
|
||||||
|
|
||||||
|
global_resource = ctx.Semaphore()
|
||||||
|
|
||||||
|
def submain(): pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
p = ctx.Process(target=submain)
|
||||||
|
p.start()
|
||||||
|
p.join()
|
||||||
|
'''))
|
||||||
|
rc, out, err = test.support.script_helper.assert_python_ok(testfn)
|
||||||
|
# on error, err = 'UserWarning: resource_tracker: There appear to
|
||||||
|
# be 1 leaked semaphore objects to clean up at shutdown'
|
||||||
|
self.assertEqual(err, b'')
|
||||||
|
|
||||||
|
|
||||||
class MiscTestCase(unittest.TestCase):
|
class MiscTestCase(unittest.TestCase):
|
||||||
def test__all__(self):
|
def test__all__(self):
|
||||||
# Just make sure names in not_exported are excluded
|
# Just make sure names in not_exported are excluded
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix a multiprocessing bug where a global named resource (such as a semaphore)
|
||||||
|
could leak when a child process is spawned (as opposed to forked).
|
Loading…
Add table
Add a link
Reference in a new issue