Skip to content

Commit

Permalink
bpo-46391: Library multiprocess leaks named resources.
Browse files Browse the repository at this point in the history
  • Loading branch information
XD Trol authored and Trol committed Jan 16, 2022
1 parent f779fac commit 055a4c2
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
14 changes: 14 additions & 0 deletions Lib/multiprocessing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ class Process(process.BaseProcess):
def _Popen(process_obj):
return _default_context.get_context().Process._Popen(process_obj)

@staticmethod
def _bootstrap_util():
return _default_context.get_context().Process._bootstrap_util()

class DefaultContext(BaseContext):
Process = Process

Expand Down Expand Up @@ -283,6 +287,11 @@ def _Popen(process_obj):
from .popen_spawn_posix import Popen
return Popen(process_obj)

@staticmethod
def _bootstrap_util():
# Process is spawned, no need to bootstrap util.
pass

class ForkServerProcess(process.BaseProcess):
_start_method = 'forkserver'
@staticmethod
Expand Down Expand Up @@ -326,6 +335,11 @@ def _Popen(process_obj):
from .popen_spawn_win32 import Popen
return Popen(process_obj)

@staticmethod
def _bootstrap_util():
# Process is spawned, no need to bootstrap util.
pass

class SpawnContext(BaseContext):
_name = 'spawn'
Process = SpawnProcess
Expand Down
10 changes: 8 additions & 2 deletions Lib/multiprocessing/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,7 @@ def _bootstrap(self, parent_sentinel=None):
if threading._HAVE_THREAD_NATIVE_ID:
threading.main_thread()._set_native_id()
try:
util._finalizer_registry.clear()
util._run_after_forkers()
self._bootstrap_util()
finally:
# delay finalization of the old process object until after
# _run_after_forkers() is executed
Expand Down Expand Up @@ -336,6 +335,13 @@ def _bootstrap(self, parent_sentinel=None):

return exitcode

@staticmethod
def _bootstrap_util():
from . import util
util._finalizer_registry.clear()
util._run_after_forkers()


#
# We subclass bytes to avoid accidental transmission of auth keys over network
#
Expand Down
35 changes: 35 additions & 0 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5706,6 +5706,41 @@ def test_namespace(self):
self.run_worker(self._test_namespace, o)


class TestNamedResource(unittest.TestCase):

def test_global_named_resource_spawn(self):
#
# Check that global named resources in main module
# will not leak by a subprocess, in spawn context.
#
import tempfile as tf
py = tf.NamedTemporaryFile('w', delete=False)
try:
py.write('''if 1:
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()
''')
py.close()

p = subprocess.run([sys.executable, py.name],
stderr=subprocess.PIPE,
text=True)

self.assertNotRegex(p.stderr, 'resource_tracker: There appear to be .* leaked')
finally:
os.unlink(py.name)


class MiscTestCase(unittest.TestCase):
def test__all__(self):
# Just make sure names in not_exported are excluded
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix that Library multiprocess leaks named resources when global named
resources are used in the main module in spawn context.

0 comments on commit 055a4c2

Please sign in to comment.