Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
gh-84559: Change the default multiprocessing start method.
We drop 'fork' in favor of 'forkserver' or 'spawn'. See the issue for details.
  • Loading branch information
gpshead committed Feb 4, 2023
commit c5277b7549f6451cb0de79ac7d60770ad3deb67c
19 changes: 11 additions & 8 deletions Lib/multiprocessing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,12 @@ def get_start_method(self, allow_none=False):

def get_all_start_methods(self):
"""Returns a list of the supported start methods, default first."""
if sys.platform == 'win32':
return ['spawn']
else:
methods = ['spawn', 'fork'] if sys.platform == 'darwin' else ['fork', 'spawn']
if reduction.HAVE_SEND_HANDLE:
methods.append('forkserver')
return methods
default = self._default_context.get_start_method()
start_method_names = [default]
start_method_names += (
name for name in _concrete_contexts if name != default
)
return start_method_names


#
Expand Down Expand Up @@ -325,7 +324,11 @@ def _check_available(self):
# on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
_default_context = DefaultContext(_concrete_contexts['spawn'])
else:
_default_context = DefaultContext(_concrete_contexts['fork'])
# gh-84559: We changed the default to a thread safe one in 3.14.
if reduction.HAVE_SEND_HANDLE:
_default_context = DefaultContext(_concrete_contexts['forkserver'])
else:
_default_context = DefaultContext(_concrete_contexts['spawn'])

else:

Expand Down
21 changes: 16 additions & 5 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5261,15 +5261,26 @@ def test_set_get(self):
multiprocessing.set_start_method(old_method, force=True)
self.assertGreaterEqual(count, 1)

def test_get_all(self):
def test_get_all_start_methods(self):
methods = multiprocessing.get_all_start_methods()
self.assertIn('spawn', methods)
if sys.platform == 'win32':
self.assertEqual(methods, ['spawn'])
if sys.platform == 'darwin':
self.assertEqual(methods[0], 'spawn') # The default is first.
# Whether these work or not, they remain available on macOS.
self.assertIn('fork', methods)
self.assertIn('forkserver', methods)
else:
self.assertTrue(methods == ['fork', 'spawn'] or
methods == ['spawn', 'fork'] or
methods == ['fork', 'spawn', 'forkserver'] or
methods == ['spawn', 'fork', 'forkserver'])
# POSIX
self.assertIn('fork', methods)
if other_methods := set(methods) - {'fork', 'spawn'}:
self.assertEqual({'forkserver'}, other_methods)
# >=3.14 Defaults to forkserver if the platform supports it.
self.assertIn(methods[0], {'forkserver', 'spawn'},
msg='3.14+ default must not be fork')
if methods[0] == 'spawn':
self.assertNotIn('forkserver', methods)

def test_preload_resources(self):
if multiprocessing.get_start_method() != 'forkserver':
Expand Down
8 changes: 7 additions & 1 deletion Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2161,7 +2161,13 @@ def skip_if_broken_multiprocessing_synchronize():
# bpo-38377: On Linux, creating a semaphore fails with OSError
# if the current user does not have the permission to create
# a file in /dev/shm/ directory.
synchronize.Lock(ctx=None)
import multiprocessing
synchronize.Lock(ctx=multiprocessing.get_context('fork'))
# The explicit fork mp context is required as relying on the
# default breaks TestResourceTracker.test_resource_tracker_reused
# when the default start method is not fork as synchronize creates
# a new multiprocessing.resource_tracker process at module import
# time via the aboe call in that scenario. This enables gh-84559.
except OSError as exc:
raise unittest.SkipTest(f"broken multiprocessing SemLock: {exc!r}")

Expand Down