Skip to content

Commit b2f8ea9

Browse files
gpsheadclaudepicnixz
authored andcommitted
gh-140814: Fix freeze_support() setting start method as side effect (GH-144608)
freeze_support() called get_start_method() without allow_none=True, which locked in the default start method context. This caused a subsequent set_start_method() call to raise "context has already been set". Use allow_none=True and accept None as a matching value, since spawn.freeze_support() independently detects spawned child processes. Test that freeze_support() does not lock in the default start method, which would prevent a subsequent set_start_method() call. (cherry picked from commit ee53180) Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent 102cd61 commit b2f8ea9

File tree

3 files changed

+24
-1
lines changed

3 files changed

+24
-1
lines changed

Lib/multiprocessing/context.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,13 @@ def freeze_support(self):
145145
'''Check whether this is a fake forked process in a frozen executable.
146146
If so then run code specified by commandline and exit.
147147
'''
148-
if self.get_start_method() == 'spawn' and getattr(sys, 'frozen', False):
148+
# gh-140814: allow_none=True avoids locking in the default start
149+
# method, which would cause a later set_start_method() to fail.
150+
# None is safe to pass through: spawn.freeze_support()
151+
# independently detects whether this process is a spawned
152+
# child, so the start method check here is only an optimization.
153+
if (getattr(sys, 'frozen', False)
154+
and self.get_start_method(allow_none=True) in ('spawn', None)):
149155
from .spawn import freeze_support
150156
freeze_support()
151157

Lib/test/_test_multiprocessing.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5707,6 +5707,20 @@ def test_spawn_dont_set_context(self):
57075707
process.join()
57085708
self.assertIsNone(multiprocessing.get_start_method(allow_none=True))
57095709

5710+
@only_run_in_spawn_testsuite("freeze_support is not start method specific")
5711+
def test_freeze_support_dont_set_context(self):
5712+
# gh-140814: freeze_support() should not set the start method
5713+
# as a side effect, so a later set_start_method() still works.
5714+
multiprocessing.set_start_method(None, force=True)
5715+
try:
5716+
multiprocessing.freeze_support()
5717+
self.assertIsNone(
5718+
multiprocessing.get_start_method(allow_none=True))
5719+
# Should not raise "context has already been set"
5720+
multiprocessing.set_start_method('spawn')
5721+
finally:
5722+
multiprocessing.set_start_method(None, force=True)
5723+
57105724
def test_context_check_module_types(self):
57115725
try:
57125726
ctx = multiprocessing.get_context('forkserver')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`multiprocessing.freeze_support` no longer sets the default start method
2+
as a side effect, which previously caused a subsequent
3+
:func:`multiprocessing.set_start_method` call to raise :exc:`RuntimeError`.

0 commit comments

Comments
 (0)