def test_shared_memory_SharedMemoryManager_basics(self): smm1 = shared_memory.SharedMemoryManager() with self.assertRaises(ValueError): smm1.SharedMemory( size=9) # Fails if SharedMemoryServer not started smm1.start() lol = [smm1.ShareableList(range(i)) for i in range(5, 10)] lom = [smm1.SharedMemory(size=j) for j in range(32, 128, 16)] doppleganger_list0 = shared_memory.ShareableList(name=lol[0].shm.name) self.assertEqual(len(doppleganger_list0), 5) doppleganger_shm0 = shared_memory.SharedMemory(name=lom[0].name) self.assertGreaterEqual(len(doppleganger_shm0.buf), 32) held_name = lom[0].name smm1.shutdown() if sys.platform != "win32": # Calls to unlink() have no effect on Windows platform; shared # memory will only be released once final process exits. with self.assertRaises(FileNotFoundError): # No longer there to be attached to again. absent_shm = shared_memory.SharedMemory(name=held_name) with shared_memory.SharedMemoryManager() as smm2: sl = smm2.ShareableList("howdy") shm = smm2.SharedMemory(size=128) held_name = sl.shm.name if sys.platform != "win32": with self.assertRaises(FileNotFoundError): # No longer there to be attached to again. absent_sl = shared_memory.ShareableList(name=held_name)
def __init__(self, shape, dtype=np.float64, name=None): """Creates a new SharedNDArray. If name is left blank, a new POSIX shared memory segment is created using a random name. Args: shape: Shape of the wrapped ndarray. dtype: Data type of the wrapped ndarray. name: Optional; the filesystem path of the underlying POSIX shared memory. Returns: A new SharedNDArray of the given shape and dtype and backed by the given optional name. Raises: SharedNDArrayError: if an error occurs. """ size = MINIMUMSHMSIZE + int(np.prod(shape)) * np.dtype(dtype).itemsize if name: try: self._shm = shared_memory.SharedMemory(name, size=size) except shared_memory.ExistentialError as ee: raise ee.__class__(f"{ee.args[0]}; requested name: {name}") else: self._shm = shared_memory.SharedMemory(None, flags=shared_memory.O_CREX, size=size) self.array = np.ndarray(shape, dtype, self._shm.buf, order='C')
def _attach_existing_shmem_then_write(shmem_name_or_obj, binary_data): if isinstance(shmem_name_or_obj, str): local_sms = shared_memory.SharedMemory(shmem_name_or_obj) else: local_sms = shmem_name_or_obj local_sms.buf[:len(binary_data)] = binary_data local_sms.close()
def test_shared_memory_across_processes(self): # bpo-40135: don't define shared memory block's name in case of # the failure when we run multiprocessing tests in parallel. sms = shared_memory.SharedMemory(create=True, size=512) self.addCleanup(sms.unlink) # Verify remote attachment to existing block by name is working. p = multiprocessing.Process( target=self._attach_existing_shmem_then_write, args=(sms.name, b'howdy')) p.daemon = True p.start() p.join() self.assertEqual(bytes(sms.buf[:5]), b'howdy') # Verify pickling of SharedMemory instance also works. p = multiprocessing.Process( target=self._attach_existing_shmem_then_write, args=(sms, b'HELLO')) p.daemon = True p.start() p.join() self.assertEqual(bytes(sms.buf[:5]), b'HELLO') sms.close()
def test_shared_memory_cleaned_after_process_termination(self): cmd = '''if 1: import os, time, sys import shared_memory # Create a shared_memory segment, and send the segment name sm = shared_memory.SharedMemory(create=True, size=10) sys.stdout.write(sm.name + '\\n') sys.stdout.flush() time.sleep(100) ''' with subprocess.Popen([sys.executable, '-E', '-c', cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: name = p.stdout.readline().strip().decode() # killing abruptly processes holding reference to a shared memory # segment should not leak the given memory segment. p.terminate() p.wait() deadline = time.monotonic() + 60 t = 0.1 while time.monotonic() < deadline: time.sleep(t) t = min(t * 2, 5) try: smm = shared_memory.SharedMemory(name, create=False) except FileNotFoundError: break else: raise AssertionError("A SharedMemory segment was leaked after" " a process was abruptly terminated.") 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)
def test_shared_memory_basics(self): sms = shared_memory.SharedMemory('test01_tsmb', create=True, size=512) self.addCleanup(sms.unlink) # Verify attributes are readable. self.assertEqual(sms.name, 'test01_tsmb') self.assertGreaterEqual(sms.size, 512) self.assertGreaterEqual(len(sms.buf), sms.size) # Modify contents of shared memory segment through memoryview. sms.buf[0] = 42 self.assertEqual(sms.buf[0], 42) # Attach to existing shared memory segment. also_sms = shared_memory.SharedMemory('test01_tsmb') self.assertEqual(also_sms.buf[0], 42) also_sms.close() # Attach to existing shared memory segment but specify a new size. same_sms = shared_memory.SharedMemory('test01_tsmb', size=20 * sms.size) self.assertLess(same_sms.size, 20 * sms.size) # Size was ignored. same_sms.close() if _USE_POSIX: # Posix Shared Memory can only be unlinked once. Here we # test an implementation detail that is not observed across # all supported platforms (since WindowsNamedSharedMemory # manages unlinking on its own and unlink() does nothing). # True release of shared memory segment does not necessarily # happen until process exits, depending on the OS platform. with self.assertRaises(FileNotFoundError): sms_uno = shared_memory.SharedMemory('test01_dblunlink', create=True, size=5000) try: self.assertGreaterEqual(sms_uno.size, 5000) sms_duo = shared_memory.SharedMemory('test01_dblunlink') sms_duo.unlink() # First shm_unlink() call. sms_duo.close() sms_uno.close() finally: sms_uno.unlink() # A second shm_unlink() call is bad. with self.assertRaises(FileExistsError): # Attempting to create a new shared memory segment with a # name that is already in use triggers an exception. there_can_only_be_one_sms = shared_memory.SharedMemory( 'test01_tsmb', create=True, size=512) if _USE_POSIX: # Requesting creation of a shared memory segment with the option # to attach to an existing segment, if that name is currently in # use, should not trigger an exception. # Note: Using a smaller size could possibly cause truncation of # the existing segment but is OS platform dependent. In the # case of MacOS/darwin, requesting a smaller size is disallowed. class OptionalAttachSharedMemory(shared_memory.SharedMemory): _flags = os.O_CREAT | os.O_RDWR ok_if_exists_sms = OptionalAttachSharedMemory('test01_tsmb') self.assertEqual(ok_if_exists_sms.size, sms.size) ok_if_exists_sms.close() # Attempting to attach to an existing shared memory segment when # no segment exists with the supplied name triggers an exception. with self.assertRaises(FileNotFoundError): nonexisting_sms = shared_memory.SharedMemory('test01_notthere') nonexisting_sms.unlink() # Error should occur on prior line. sms.close() # Test creating a shared memory segment with negative size with self.assertRaises(ValueError): sms_invalid = shared_memory.SharedMemory(create=True, size=-1) # Test creating a shared memory segment with size 0 with self.assertRaises(ValueError): sms_invalid = shared_memory.SharedMemory(create=True, size=0) # Test creating a shared memory segment without size argument with self.assertRaises(ValueError): sms_invalid = shared_memory.SharedMemory(create=True)