@@ -607,6 +607,10 @@ def __init__(self, max_workers=None, mp_context=None,
607
607
mp_context = mp .get_context ()
608
608
self ._mp_context = mp_context
609
609
610
+ # https://github.com/python/cpython/issues/90622
611
+ self ._safe_to_dynamically_spawn_children = (
612
+ self ._mp_context .get_start_method (allow_none = False ) != "fork" )
613
+
610
614
if initializer is not None and not callable (initializer ):
611
615
raise TypeError ("initializer must be a callable" )
612
616
self ._initializer = initializer
@@ -657,6 +661,8 @@ def __init__(self, max_workers=None, mp_context=None,
657
661
def _start_executor_manager_thread (self ):
658
662
if self ._executor_manager_thread is None :
659
663
# Start the processes so that their sentinels are known.
664
+ if not self ._safe_to_dynamically_spawn_children : # ie, using fork.
665
+ self ._launch_processes ()
660
666
self ._executor_manager_thread = _ExecutorManagerThread (self )
661
667
self ._executor_manager_thread .start ()
662
668
_threads_wakeups [self ._executor_manager_thread ] = \
@@ -669,14 +675,31 @@ def _adjust_process_count(self):
669
675
670
676
process_count = len (self ._processes )
671
677
if process_count < self ._max_workers :
672
- p = self ._mp_context .Process (
673
- target = _process_worker ,
674
- args = (self ._call_queue ,
675
- self ._result_queue ,
676
- self ._initializer ,
677
- self ._initargs ))
678
- p .start ()
679
- self ._processes [p .pid ] = p
678
+ # Assertion disabled as this codepath is also used to replace a
679
+ # worker that unexpectedly dies, even when using the 'fork' start
680
+ # method. That means there is still a potential deadlock bug. If a
681
+ # 'fork' mp_context worker dies, we'll be forking a new one when
682
+ # we know a thread is running (self._executor_manager_thread).
683
+ #assert self._safe_to_dynamically_spawn_children or not self._executor_manager_thread, 'https://github.com/python/cpython/issues/90622'
684
+ self ._spawn_process ()
685
+
686
+ def _launch_processes (self ):
687
+ # https://github.com/python/cpython/issues/90622
688
+ assert not self ._executor_manager_thread , (
689
+ 'Processes cannot be fork()ed after the thread has started, '
690
+ 'deadlock in the child processes could result.' )
691
+ for _ in range (len (self ._processes ), self ._max_workers ):
692
+ self ._spawn_process ()
693
+
694
+ def _spawn_process (self ):
695
+ p = self ._mp_context .Process (
696
+ target = _process_worker ,
697
+ args = (self ._call_queue ,
698
+ self ._result_queue ,
699
+ self ._initializer ,
700
+ self ._initargs ))
701
+ p .start ()
702
+ self ._processes [p .pid ] = p
680
703
681
704
def submit (self , fn , / , * args , ** kwargs ):
682
705
with self ._shutdown_lock :
@@ -697,7 +720,8 @@ def submit(self, fn, /, *args, **kwargs):
697
720
# Wake up queue management thread
698
721
self ._executor_manager_thread_wakeup .wakeup ()
699
722
700
- self ._adjust_process_count ()
723
+ if self ._safe_to_dynamically_spawn_children :
724
+ self ._adjust_process_count ()
701
725
self ._start_executor_manager_thread ()
702
726
return f
703
727
submit .__doc__ = _base .Executor .submit .__doc__
0 commit comments