From c123d1a54880e39de81737afc2e8c6971875b98f Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 31 Jul 2026 20:55:30 +0100 Subject: [PATCH 1/3] Don't keep file descriptor in SharedMemory object --- Lib/multiprocessing/shared_memory.py | 23 +++++++------------ ...-07-31-20-54-50.gh-issue-155003.rgWjVC.rst | 3 +++ 2 files changed, 11 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-31-20-54-50.gh-issue-155003.rgWjVC.rst diff --git a/Lib/multiprocessing/shared_memory.py b/Lib/multiprocessing/shared_memory.py index 99a8ce3320ad4e..e84ba35566ee6e 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 @@ -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,16 @@ 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) + 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 +186,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 +227,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..24d1fb4053977d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-31-20-54-50.gh-issue-155003.rgWjVC.rst @@ -0,0 +1,3 @@ +:cls:`multiprocessing.shared_memory.SharedMemory` no longer holds on to a +file descriptor on Posix platforms, and does not forcibly close its buffer +when finalized. From fc454e9d8b27c75ec2ff550d97432424b471344f Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 31 Jul 2026 21:18:20 +0100 Subject: [PATCH 2/3] Fix cross-reference in news entry --- .../next/Library/2026-07-31-20-54-50.gh-issue-155003.rgWjVC.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 24d1fb4053977d..279d307e58b486 100644 --- 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 @@ -1,3 +1,3 @@ -:cls:`multiprocessing.shared_memory.SharedMemory` no longer holds on to a +: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. From 08584a7e97573746784e52c2873bfa21a49f1e58 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Sat, 1 Aug 2026 10:19:57 +0100 Subject: [PATCH 3/3] Optionally use posix_fallocate() if available --- Lib/multiprocessing/shared_memory.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/shared_memory.py b/Lib/multiprocessing/shared_memory.py index e84ba35566ee6e..fcdcddb75ad9f9 100644 --- a/Lib/multiprocessing/shared_memory.py +++ b/Lib/multiprocessing/shared_memory.py @@ -72,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: @@ -110,7 +110,11 @@ def __init__(self, name=None, create=False, size=0, *, track=True): self._name = name try: if create and size: - os.ftruncate(fd, size) + 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(fd, size)