Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Lib/multiprocessing/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def arbitrary_address(family):
if family == 'AF_INET':
return ('localhost', 0)
elif family == 'AF_UNIX':
return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir())
return tempfile.mktemp(prefix='sock-', dir=util.get_temp_dir())
elif family == 'AF_PIPE':
return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' %
(os.getpid(), next(_mmap_counter)), dir="")
Expand Down
77 changes: 76 additions & 1 deletion Lib/multiprocessing/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
DEBUG = 10
INFO = 20
SUBWARNING = 25
WARNING = 30

LOGGER_NAME = 'multiprocessing'
DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s'
Expand All @@ -53,6 +54,10 @@ def info(msg, *args):
if _logger:
_logger.log(INFO, msg, *args, stacklevel=2)

def _warn(msg, *args):
if _logger:
_logger.log(WARNING, msg, *args, stacklevel=2)

def sub_warning(msg, *args):
if _logger:
_logger.log(SUBWARNING, msg, *args, stacklevel=2)
Expand Down Expand Up @@ -121,6 +126,21 @@ def is_abstract_socket_namespace(address):
# Function returning a temp directory which will be removed on exit
#

# Maximum length of a socket file path is usually between 92 and 108 [1],
# but Linux is known to use a size of 108 [2]. BSD-based systems usually
# use a size of 104 or 108 and Windows does not create AF_UNIX sockets.
#
# [1]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html
# [2]: https://man7.org/linux/man-pages/man7/unix.7.html.

if sys.platform == 'linux':
_SUN_PATH_MAX = 108
elif sys.platform.startswith(('openbsd', 'freebsd')):
_SUN_PATH_MAX = 104
else:
# On Windows platforms, we do not create AF_UNIX sockets.
_SUN_PATH_MAX = None if os.name == 'nt' else 92

def _remove_temp_dir(rmtree, tempdir):
rmtree(tempdir)

Expand All @@ -130,12 +150,67 @@ def _remove_temp_dir(rmtree, tempdir):
if current_process is not None:
current_process._config['tempdir'] = None

def _get_base_temp_dir(tempfile):
"""Get a temporary directory where socket files will be created.

To prevent additional imports, pass a pre-imported 'tempfile' module.
"""
if os.name == 'nt':
return None
# Most of the time, the default temporary directory is /tmp. Thus,
# listener sockets files "$TMPDIR/pymp-XXXXXXXX/sock-XXXXXXXX" do
# not have a path length exceeding SUN_PATH_MAX.
#
# If users specify their own temporary directory, we may be unable
# to create those files. Therefore, we fall back to the system-wide
# temporary directory /tmp, assumed to exist on POSIX systems.
#
# See https://github.com/python/cpython/issues/132124.
base_tempdir = tempfile.gettempdir()
# Files created in a temporary directory are suffixed by a string
# generated by tempfile._RandomNameSequence, which, by design,
# is 8 characters long.
#
# Thus, the length of socket filename will be:
#
# len(base_tempdir + '/pymp-XXXXXXXX' + '/sock-XXXXXXXX')
sun_path_len = len(base_tempdir) + 14 + 14
if sun_path_len <= _SUN_PATH_MAX:
return base_tempdir
# Fallback to the default system-wide temporary directory.
# This ignores user-defined environment variables.
#
# On POSIX systems, /tmp MUST be writable by any application [1].
# We however emit a warning if this is not the case to prevent
# obscure errors later in the execution.
#
# On some legacy systems, /var/tmp and /usr/tmp can be present
# and will be used instead.
#
# [1]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s18.html
dirlist = ['/tmp', '/var/tmp', '/usr/tmp']
try:
base_system_tempdir = tempfile._get_default_tempdir(dirlist)
except FileNotFoundError:
_warn("Process-wide temporary directory %s will not be usable for "
"creating socket files and no usable system-wide temporary "
"directory was found in %s", base_tempdir, dirlist)
# At this point, the system-wide temporary directory is not usable
# but we may assume that the user-defined one is, even if we will
# not be able to write socket files out there.
return base_tempdir
_warn("Ignoring user-defined temporary directory: %s", base_tempdir)
# at most max(map(len, dirlist)) + 14 + 14 = 36 characters
assert len(base_system_tempdir) + 14 + 14 <= _SUN_PATH_MAX
return base_system_tempdir

def get_temp_dir():
# get name of a temp directory which will be automatically cleaned up
tempdir = process.current_process()._config.get('tempdir')
if tempdir is None:
import shutil, tempfile
tempdir = tempfile.mkdtemp(prefix='pymp-')
base_tempdir = _get_base_temp_dir(tempfile)
tempdir = tempfile.mkdtemp(prefix='pymp-', dir=base_tempdir)
info('created temp directory %s', tempdir)
# keep a strong reference to shutil.rmtree(), since the finalizer
# can be called late during Python shutdown
Expand Down
5 changes: 3 additions & 2 deletions Lib/tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def _candidate_tempdir_list():

return dirlist

def _get_default_tempdir():
def _get_default_tempdir(dirlist=None):
"""Calculate the default directory to use for temporary files.
This routine should be called exactly once.

Expand All @@ -190,7 +190,8 @@ def _get_default_tempdir():
service, the name of the test file must be randomized."""

namer = _RandomNameSequence()
dirlist = _candidate_tempdir_list()
if dirlist is None:
dirlist = _candidate_tempdir_list()

for dir in dirlist:
if dir != _os.curdir:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
On POSIX-compliant systems, :func:`!multiprocessing.util.get_temp_dir` now
ignores :envvar:`TMPDIR` (and similar environment variables) if the path
length of ``AF_UNIX`` socket files exceeds the platform-specific maximum
length when using the :ref:`forkserver
<multiprocessing-start-method-forkserver>` start method. Patch by Bénédikt
Tran.
Loading