@@ -613,11 +613,16 @@ PyOS_BeforeFork(void)
613613 run_at_forkers (interp -> before_forkers , 1 );
614614
615615 _PyImport_AcquireLock (interp );
616+ _PyEval_StopTheWorldAll (& _PyRuntime );
617+ HEAD_LOCK (& _PyRuntime );
616618}
617619
618620void
619621PyOS_AfterFork_Parent (void )
620622{
623+ HEAD_UNLOCK (& _PyRuntime );
624+ _PyEval_StartTheWorldAll (& _PyRuntime );
625+
621626 PyInterpreterState * interp = _PyInterpreterState_GET ();
622627 if (_PyImport_ReleaseLock (interp ) <= 0 ) {
623628 Py_FatalError ("failed releasing import lock after fork" );
@@ -632,6 +637,7 @@ PyOS_AfterFork_Child(void)
632637 PyStatus status ;
633638 _PyRuntimeState * runtime = & _PyRuntime ;
634639
640+ // re-creates runtime->interpreters.mutex (HEAD_UNLOCK)
635641 status = _PyRuntimeState_ReInitThreads (runtime );
636642 if (_PyStatus_EXCEPTION (status )) {
637643 goto fatal_error ;
@@ -7731,10 +7737,15 @@ os_register_at_fork_impl(PyObject *module, PyObject *before,
77317737// running in the process. Best effort, silent if unable to count threads.
77327738// Constraint: Quick. Never overcounts. Never leaves an error set.
77337739//
7734- // This code might do an import, thus acquiring the import lock, which
7735- // PyOS_BeforeFork() also does. As this should only be called from
7736- // the parent process, it is in the same thread so that works.
7737- static void warn_about_fork_with_threads (const char * name ) {
7740+ // This should only be called from the parent process after
7741+ // PyOS_AfterFork_Parent().
7742+ static void
7743+ warn_about_fork_with_threads (const char * name )
7744+ {
7745+ // It's not safe to issue the warning while the world is stopped, because
7746+ // other threads might be holding locks that we need, which would deadlock.
7747+ assert (!_PyRuntime .stoptheworld .world_stopped );
7748+
77387749 // TODO: Consider making an `os` module API to return the current number
77397750 // of threads in the process. That'd presumably use this platform code but
77407751 // raise an error rather than using the inaccurate fallback.
@@ -7858,9 +7869,10 @@ os_fork1_impl(PyObject *module)
78587869 /* child: this clobbers and resets the import lock. */
78597870 PyOS_AfterFork_Child ();
78607871 } else {
7861- warn_about_fork_with_threads ("fork1" );
78627872 /* parent: release the import lock. */
78637873 PyOS_AfterFork_Parent ();
7874+ // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
7875+ warn_about_fork_with_threads ("fork1" );
78647876 }
78657877 if (pid == -1 ) {
78667878 errno = saved_errno ;
@@ -7906,9 +7918,10 @@ os_fork_impl(PyObject *module)
79067918 /* child: this clobbers and resets the import lock. */
79077919 PyOS_AfterFork_Child ();
79087920 } else {
7909- warn_about_fork_with_threads ("fork" );
79107921 /* parent: release the import lock. */
79117922 PyOS_AfterFork_Parent ();
7923+ // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
7924+ warn_about_fork_with_threads ("fork" );
79127925 }
79137926 if (pid == -1 ) {
79147927 errno = saved_errno ;
@@ -8737,9 +8750,10 @@ os_forkpty_impl(PyObject *module)
87378750 /* child: this clobbers and resets the import lock. */
87388751 PyOS_AfterFork_Child ();
87398752 } else {
8740- warn_about_fork_with_threads ("forkpty" );
87418753 /* parent: release the import lock. */
87428754 PyOS_AfterFork_Parent ();
8755+ // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
8756+ warn_about_fork_with_threads ("forkpty" );
87438757 }
87448758 if (pid == -1 ) {
87458759 return posix_error ();
0 commit comments