Skip to content

Commit 2ad51c6

Browse files
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 30610d2) Co-authored-by: Leo Trol <milestone.jxd@gmail.com>
1 parent 8d8251a commit 2ad51c6

File tree

4 files changed

+54
-2
lines changed

4 files changed

+54
-2
lines changed

Lib/multiprocessing/context.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ class Process(process.BaseProcess):
223223
def _Popen(process_obj):
224224
return _default_context.get_context().Process._Popen(process_obj)
225225

226+
@staticmethod
227+
def _after_fork():
228+
return _default_context.get_context().Process._after_fork()
229+
226230
class DefaultContext(BaseContext):
227231
Process = Process
228232

@@ -283,6 +287,11 @@ def _Popen(process_obj):
283287
from .popen_spawn_posix import Popen
284288
return Popen(process_obj)
285289

290+
@staticmethod
291+
def _after_fork():
292+
# process is spawned, nothing to do
293+
pass
294+
286295
class ForkServerProcess(process.BaseProcess):
287296
_start_method = 'forkserver'
288297
@staticmethod
@@ -326,6 +335,11 @@ def _Popen(process_obj):
326335
from .popen_spawn_win32 import Popen
327336
return Popen(process_obj)
328337

338+
@staticmethod
339+
def _after_fork():
340+
# process is spawned, nothing to do
341+
pass
342+
329343
class SpawnContext(BaseContext):
330344
_name = 'spawn'
331345
Process = SpawnProcess

Lib/multiprocessing/process.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,7 @@ def _bootstrap(self, parent_sentinel=None):
304304
if threading._HAVE_THREAD_NATIVE_ID:
305305
threading.main_thread()._set_native_id()
306306
try:
307-
util._finalizer_registry.clear()
308-
util._run_after_forkers()
307+
self._after_fork()
309308
finally:
310309
# delay finalization of the old process object until after
311310
# _run_after_forkers() is executed
@@ -336,6 +335,13 @@ def _bootstrap(self, parent_sentinel=None):
336335

337336
return exitcode
338337

338+
@staticmethod
339+
def _after_fork():
340+
from . import util
341+
util._finalizer_registry.clear()
342+
util._run_after_forkers()
343+
344+
339345
#
340346
# We subclass bytes to avoid accidental transmission of auth keys over network
341347
#

Lib/test/_test_multiprocessing.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import unittest
66
import unittest.mock
77
import queue as pyqueue
8+
import textwrap
89
import time
910
import io
1011
import itertools
@@ -5699,6 +5700,35 @@ def test_namespace(self):
56995700
self.run_worker(self._test_namespace, o)
57005701

57015702

5703+
class TestNamedResource(unittest.TestCase):
5704+
def test_global_named_resource_spawn(self):
5705+
#
5706+
# gh-90549: Check that global named resources in main module
5707+
# will not leak by a subprocess, in spawn context.
5708+
#
5709+
testfn = os_helper.TESTFN
5710+
self.addCleanup(os_helper.unlink, testfn)
5711+
with open(testfn, 'w', encoding='utf-8') as f:
5712+
f.write(textwrap.dedent('''\
5713+
import multiprocessing as mp
5714+
5715+
ctx = mp.get_context('spawn')
5716+
5717+
global_resource = ctx.Semaphore()
5718+
5719+
def submain(): pass
5720+
5721+
if __name__ == '__main__':
5722+
p = ctx.Process(target=submain)
5723+
p.start()
5724+
p.join()
5725+
'''))
5726+
rc, out, err = test.support.script_helper.assert_python_ok(testfn)
5727+
# on error, err = 'UserWarning: resource_tracker: There appear to
5728+
# be 1 leaked semaphore objects to clean up at shutdown'
5729+
self.assertEqual(err, b'')
5730+
5731+
57025732
class MiscTestCase(unittest.TestCase):
57035733
def test__all__(self):
57045734
# Just make sure names in not_exported are excluded
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a multiprocessing bug where a global named resource (such as a semaphore)
2+
could leak when a child process is spawned (as opposed to forked).

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy