Skip to content

Commit

Permalink
Re-sync with internal repository
Browse files Browse the repository at this point in the history
The internal and external repositories are out of sync. This Pull Request attempts to brings them back in sync by patching the GitHub repository. Please carefully review this patch. You must disable ShipIt for your project in order to merge this pull request. DO NOT IMPORT this pull request. Instead, merge it directly on GitHub using the MERGE BUTTON. Re-enable ShipIt after merging.
  • Loading branch information
facebook-github-bot committed Jun 12, 2024
1 parent c9840b4 commit 4b80658
Show file tree
Hide file tree
Showing 1,850 changed files with 57,301 additions and 686,208 deletions.
51 changes: 35 additions & 16 deletions Doc/library/multiprocessing.shared_memory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ or other communications requiring the serialization/deserialization and
copying of data.


.. class:: SharedMemory(name=None, create=False, size=0)
.. class:: SharedMemory(name=None, create=False, size=0, *, track=True)

Creates a new shared memory block or attaches to an existing shared
memory block. Each shared memory block is assigned a unique name.
Expand Down Expand Up @@ -64,26 +64,45 @@ copying of data.
memory block may be larger or equal to the size requested. When attaching
to an existing shared memory block, the ``size`` parameter is ignored.

*track*, when enabled, registers the shared memory block with a resource
tracker process on platforms where the OS does not do this automatically.
The resource tracker ensures proper cleanup of the shared memory even
if all other processes with access to the memory exit without doing so.
Python processes created from a common ancestor using :mod:`multiprocessing`
facilities share a single resource tracker process, and the lifetime of
shared memory segments is handled automatically among these processes.
Python processes created in any other way will receive their own
resource tracker when accessing shared memory with *track* enabled.
This will cause the shared memory to be deleted by the resource tracker
of the first process that terminates.
To avoid this issue, users of :mod:`subprocess` or standalone Python
processes should set *track* to ``False`` when there is already another
process in place that does the bookkeeping.
*track* is ignored on Windows, which has its own tracking and
automatically deletes shared memory when all handles to it have been closed.

.. versionchanged:: 3.13 Added *track* parameter.

.. method:: close()

Closes access to the shared memory from this instance. In order to
ensure proper cleanup of resources, all instances should call
``close()`` once the instance is no longer needed. Note that calling
``close()`` does not cause the shared memory block itself to be
destroyed.
Closes the file descriptor/handle to the shared memory from this
instance. :meth:`close()` should be called once access to the shared
memory block from this instance is no longer needed. Depending
on operating system, the underlying memory may or may not be freed
even if all handles to it have been closed. To ensure proper cleanup,
use the :meth:`unlink()` method.

.. method:: unlink()

Requests that the underlying shared memory block be destroyed. In
order to ensure proper cleanup of resources, ``unlink()`` should be
called once (and only once) across all processes which have need
for the shared memory block. After requesting its destruction, a
shared memory block may or may not be immediately destroyed and
this behavior may differ across platforms. Attempts to access data
inside the shared memory block after ``unlink()`` has been called may
result in memory access errors. Note: the last process relinquishing
its hold on a shared memory block may call ``unlink()`` and
:meth:`close()` in either order.
Deletes the underlying shared memory block. This should be called only
once per shared memory block regardless of the number of handles to it,
even in other processes.
:meth:`unlink()` and :meth:`close()` can be called in any order, but
trying to access data inside a shared memory block after :meth:`unlink()`
may result in memory access errors, depending on platform.

This method has no effect on Windows, where the only way to delete a
shared memory block is to close all handles.

.. attribute:: buf

Expand Down
6 changes: 3 additions & 3 deletions Lib/lib2to3/tests/data/crlf.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
print "hi"

print "Like bad Windows newlines?"
print "hi"

print "Like bad Windows newlines?"
26 changes: 18 additions & 8 deletions Lib/multiprocessing/shared_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ class SharedMemory:
_flags = os.O_RDWR
_mode = 0o600
_prepend_leading_slash = True if _USE_POSIX else False
_track = True

def __init__(self, name=None, create=False, size=0):
def __init__(self, name=None, create=False, size=0, *, track=True):
if not size >= 0:
raise ValueError("'size' must be a positive integer")
if create:
Expand All @@ -81,6 +82,7 @@ def __init__(self, name=None, create=False, size=0):
if name is None and not self._flags & os.O_EXCL:
raise ValueError("'name' can only be None if create=True")

self._track = track
if _USE_POSIX:

# POSIX Shared Memory
Expand Down Expand Up @@ -116,8 +118,9 @@ def __init__(self, name=None, create=False, size=0):
self.unlink()
raise

from .resource_tracker import register
register(self._name, "shared_memory")
if self._track:
from .resource_tracker import register
register(self._name, "shared_memory")

else:

Expand Down Expand Up @@ -233,13 +236,20 @@ def close(self):
def unlink(self):
"""Requests that the underlying shared memory block be destroyed.
In order to ensure proper cleanup of resources, unlink should be
called once (and only once) across all processes which have access
to the shared memory block."""
Unlink should be called once (and only once) across all handles
which have access to the shared memory block, even if these
handles belong to different processes. Closing and unlinking may
happen in any order, but trying to access data inside a shared
memory block after unlinking may result in memory errors,
depending on platform.
This method has no effect on Windows, where the only way to
delete a shared memory block is to close all handles."""
if _USE_POSIX and self._name:
from .resource_tracker import unregister
_posixshmem.shm_unlink(self._name)
unregister(self._name, "shared_memory")
if self._track:
from .resource_tracker import unregister
unregister(self._name, "shared_memory")


_encoding = "utf8"
Expand Down
53 changes: 53 additions & 0 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4280,6 +4280,59 @@ def test_shared_memory_cleaned_after_process_termination(self):
"resource_tracker: There appear to be 1 leaked "
"shared_memory objects to clean up at shutdown", err)

@unittest.skipIf(os.name != "posix", "resource_tracker is posix only")
def test_shared_memory_untracking(self):
# gh-82300: When a separate Python process accesses shared memory
# with track=False, it must not cause the memory to be deleted
# when terminating.
cmd = '''if 1:
import sys
from multiprocessing.shared_memory import SharedMemory
mem = SharedMemory(create=False, name=sys.argv[1], track=False)
mem.close()
'''
mem = shared_memory.SharedMemory(create=True, size=10)
# The resource tracker shares pipes with the subprocess, and so
# err existing means that the tracker process has terminated now.
try:
rc, out, err = test.support.script_helper.assert_python_ok("-c", cmd, mem.name)
self.assertNotIn(b"resource_tracker", err)
self.assertEqual(rc, 0)
mem2 = shared_memory.SharedMemory(create=False, name=mem.name)
mem2.close()
finally:
try:
mem.unlink()
except OSError:
pass
mem.close()

@unittest.skipIf(os.name != "posix", "resource_tracker is posix only")
def test_shared_memory_tracking(self):
# gh-82300: When a separate Python process accesses shared memory
# with track=True, it must cause the memory to be deleted when
# terminating.
cmd = '''if 1:
import sys
from multiprocessing.shared_memory import SharedMemory
mem = SharedMemory(create=False, name=sys.argv[1], track=True)
mem.close()
'''
mem = shared_memory.SharedMemory(create=True, size=10)
try:
rc, out, err = test.support.script_helper.assert_python_ok("-c", cmd, mem.name)
self.assertEqual(rc, 0)
self.assertIn(
b"resource_tracker: There appear to be 1 leaked "
b"shared_memory objects to clean up at shutdown", err)
finally:
try:
mem.unlink()
except OSError:
pass
resource_tracker.unregister(mem._name, "shared_memory")
mem.close()

#
# Test to verify that `Finalize` works.
#
Expand Down
8 changes: 4 additions & 4 deletions Lib/test/coding20731.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#coding:latin1


#coding:latin1



Loading

0 comments on commit 4b80658

Please sign in to comment.