bpo-36867: Create the resource_tracker before launching SharedMemoryManagers (GH-13276)

This commit is contained in:
Pierre Glaser 2019-05-13 21:15:32 +02:00 committed by Antoine Pitrou
parent be6939fb02
commit b1dfcad6f0
3 changed files with 54 additions and 14 deletions

View File

@ -21,6 +21,7 @@ import signal
import array import array
import queue import queue
import time import time
import os
from os import getpid from os import getpid
from traceback import format_exc from traceback import format_exc
@ -1349,6 +1350,14 @@ if HAS_SHMEM:
_Server = SharedMemoryServer _Server = SharedMemoryServer
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if os.name == "posix":
# bpo-36867: Ensure the resource_tracker is running before
# launching the manager process, so that concurrent
# shared_memory manipulation both in the manager and in the
# current process does not create two resource_tracker
# processes.
from . import resource_tracker
resource_tracker.ensure_running()
BaseManager.__init__(self, *args, **kwargs) BaseManager.__init__(self, *args, **kwargs)
util.debug(f"{self.__class__.__name__} created by pid {getpid()}") util.debug(f"{self.__class__.__name__} created by pid {getpid()}")

View File

@ -17,6 +17,7 @@ import array
import socket import socket
import random import random
import logging import logging
import subprocess
import struct import struct
import operator import operator
import pickle import pickle
@ -3765,6 +3766,27 @@ class _TestSharedMemory(BaseTestCase):
smm.shutdown() smm.shutdown()
@unittest.skipIf(os.name != "posix", "resource_tracker is posix only")
def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self):
# bpo-36867: test that a SharedMemoryManager uses the
# same resource_tracker process as its parent.
cmd = '''if 1:
from multiprocessing.managers import SharedMemoryManager
smm = SharedMemoryManager()
smm.start()
sl = smm.ShareableList(range(10))
smm.shutdown()
'''
rc, out, err = test.support.script_helper.assert_python_ok('-c', cmd)
# Before bpo-36867 was fixed, a SharedMemoryManager not using the same
# resource_tracker process as its parent would make the parent's
# tracker complain about sl being leaked even though smm.shutdown()
# properly released sl.
self.assertFalse(err)
def test_shared_memory_SharedMemoryManager_basics(self): def test_shared_memory_SharedMemoryManager_basics(self):
smm1 = multiprocessing.managers.SharedMemoryManager() smm1 = multiprocessing.managers.SharedMemoryManager()
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
@ -3904,8 +3926,6 @@ class _TestSharedMemory(BaseTestCase):
sl.shm.close() sl.shm.close()
def test_shared_memory_cleaned_after_process_termination(self): def test_shared_memory_cleaned_after_process_termination(self):
import subprocess
from multiprocessing import shared_memory
cmd = '''if 1: cmd = '''if 1:
import os, time, sys import os, time, sys
from multiprocessing import shared_memory from multiprocessing import shared_memory
@ -3916,18 +3936,29 @@ class _TestSharedMemory(BaseTestCase):
sys.stdout.flush() sys.stdout.flush()
time.sleep(100) time.sleep(100)
''' '''
p = subprocess.Popen([sys.executable, '-E', '-c', cmd], with subprocess.Popen([sys.executable, '-E', '-c', cmd],
stdout=subprocess.PIPE) stdout=subprocess.PIPE,
name = p.stdout.readline().strip().decode() stderr=subprocess.PIPE) as p:
name = p.stdout.readline().strip().decode()
# killing abruptly processes holding reference to a shared memory # killing abruptly processes holding reference to a shared memory
# segment should not leak the given memory segment. # segment should not leak the given memory segment.
p.terminate() p.terminate()
p.wait() p.wait()
time.sleep(1.0) # wait for the OS to collect the segment time.sleep(1.0) # wait for the OS to collect the segment
with self.assertRaises(FileNotFoundError): # The shared memory file was deleted.
smm = shared_memory.SharedMemory(name, create=False) with self.assertRaises(FileNotFoundError):
smm = shared_memory.SharedMemory(name, create=False)
if os.name == 'posix':
# A warning was emitted by the subprocess' own
# resource_tracker (on Windows, shared memory segments
# are released automatically by the OS).
err = p.stderr.read().decode()
self.assertIn(
"resource_tracker: There appear to be 1 leaked "
"shared_memory objects to clean up at shutdown", err)
# #
# #
@ -4560,7 +4591,7 @@ class TestFlags(unittest.TestCase):
print(json.dumps(flags)) print(json.dumps(flags))
def test_flags(self): def test_flags(self):
import json, subprocess import json
# start child process using unusual flags # start child process using unusual flags
prog = ('from test._test_multiprocessing import TestFlags; ' + prog = ('from test._test_multiprocessing import TestFlags; ' +
'TestFlags.run_in_child()') 'TestFlags.run_in_child()')
@ -4866,7 +4897,6 @@ class TestResourceTracker(unittest.TestCase):
# #
# Check that killing process does not leak named semaphores # Check that killing process does not leak named semaphores
# #
import subprocess
cmd = '''if 1: cmd = '''if 1:
import time, os, tempfile import time, os, tempfile
import multiprocessing as mp import multiprocessing as mp

View File

@ -0,0 +1 @@
Fix a bug making a SharedMemoryManager instance and its parent process use two separate resource_tracker processes.