diff --git a/Lib/multiprocessing/shared_memory.py b/Lib/multiprocessing/shared_memory.py index 99a8ce3320ad4e..fcdcddb75ad9f9 100644 --- a/Lib/multiprocessing/shared_memory.py +++ b/Lib/multiprocessing/shared_memory.py @@ -65,7 +65,6 @@ class SharedMemory: # Defaults; enables close() and unlink() to run without errors. _name = None - _fd = -1 _mmap = None _buf = None _flags = os.O_RDWR @@ -73,7 +72,7 @@ class SharedMemory: _prepend_leading_slash = True if _USE_POSIX else False _track = True - def __init__(self, name=None, create=False, size=0, *, track=True): + def __init__(self, name=None, create=False, size=0, *, track=True, reserve=False): if not size >= 0: raise ValueError("'size' must be a positive integer") if create: @@ -92,7 +91,7 @@ def __init__(self, name=None, create=False, size=0, *, track=True): while True: name = _make_filename() try: - self._fd = _posixshmem.shm_open( + fd = _posixshmem.shm_open( name, self._flags, mode=self._mode @@ -103,7 +102,7 @@ def __init__(self, name=None, create=False, size=0, *, track=True): break else: name = "/" + name if self._prepend_leading_slash else name - self._fd = _posixshmem.shm_open( + fd = _posixshmem.shm_open( name, self._flags, mode=self._mode @@ -111,13 +110,20 @@ def __init__(self, name=None, create=False, size=0, *, track=True): self._name = name try: if create and size: - os.ftruncate(self._fd, size) - stats = os.fstat(self._fd) + if hasattr(os, 'posix_fallocate') and reserve: + # Ensures the requested size is available + os.posix_fallocate(fd, 0, size) + else: + os.ftruncate(fd, size) + stats = os.fstat(fd) size = stats.st_size - self._mmap = mmap.mmap(self._fd, size) + self._mmap = mmap.mmap(fd, size) except OSError: self.unlink() raise + finally: + os.close(fd) + if self._track: resource_tracker.register(self._name, "shared_memory") @@ -184,12 +190,6 @@ def __init__(self, name=None, create=False, size=0, *, track=True): self._size = size self._buf = memoryview(self._mmap) - def __del__(self): - try: - self.close() - except OSError: - pass - def __reduce__(self): return ( self.__class__, @@ -231,9 +231,6 @@ def close(self): if self._mmap is not None: self._mmap.close() self._mmap = None - if _USE_POSIX and self._fd >= 0: - os.close(self._fd) - self._fd = -1 def unlink(self): """Requests that the underlying shared memory block be destroyed. diff --git a/Misc/NEWS.d/next/Library/2026-07-31-20-54-50.gh-issue-155003.rgWjVC.rst b/Misc/NEWS.d/next/Library/2026-07-31-20-54-50.gh-issue-155003.rgWjVC.rst new file mode 100644 index 00000000000000..279d307e58b486 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-31-20-54-50.gh-issue-155003.rgWjVC.rst @@ -0,0 +1,3 @@ +:class:`multiprocessing.shared_memory.SharedMemory` no longer holds on to a +file descriptor on Posix platforms, and does not forcibly close its buffer +when finalized.