@@ -723,7 +723,9 @@ PyOS_BeforeFork(void)
723723 _PyImport_AcquireLock(interp);
724724 _PyEval_StopTheWorldAll(&_PyRuntime);
725725 HEAD_LOCK(&_PyRuntime);
726- _PyWarnings_BeforeFork(interp);
726+ for (PyInterpreterState *i = PyInterpreterState_Head(); i != NULL; i = i->next) {
727+ _PyWarnings_BeforeFork(i);
728+ }
727729}
728730
729731void
@@ -753,6 +755,78 @@ reset_asyncio_state(_PyThreadStateImpl *tstate)
753755 tstate->asyncio_running_task = NULL;
754756}
755757
758+ static PyStatus
759+ clear_threads_after_fork(PyThreadState *tstate)
760+ {
761+ struct after_fork_state {
762+ PyThreadState *tstate;
763+ PyThreadState *deleted;
764+ PyThreadState *running_main;
765+ };
766+
767+ size_t count = 0;
768+ for (PyInterpreterState *interp = PyInterpreterState_Head(); interp != NULL;
769+ interp = interp->next) {
770+ count++;
771+ }
772+ struct after_fork_state *states = PyMem_RawCalloc(count, sizeof(*states));
773+ if (states == NULL) {
774+ return _PyStatus_NO_MEMORY();
775+ }
776+
777+ // Unlink all vanished threads before running any finalizers. Keep the
778+ // subinterpreters busy until their dead thread states have been cleared.
779+ size_t next = 1;
780+ for (PyInterpreterState *interp = PyInterpreterState_Head(); interp != NULL;
781+ interp = interp->next) {
782+ size_t index = (interp == tstate->interp) ? 0 : next++;
783+ struct after_fork_state *state = &states[index];
784+ state->running_main = interp->threads.main;
785+ if (state->running_main != NULL
786+ && state->running_main->thread_id != tstate->thread_id) {
787+ state->running_main = NULL;
788+ }
789+ state->tstate = (index == 0) ? tstate : PyThreadState_New(interp);
790+ if (state->tstate == NULL) {
791+ PyMem_RawFree(states);
792+ return _PyStatus_NO_MEMORY();
793+ }
794+ state->deleted = _PyThreadState_RemoveExcept(state->tstate,
795+ /*is_after_fork=*/1);
796+ if (index != 0) {
797+ _Py_atomic_store_ptr(&interp->threads.main, state->tstate);
798+ }
799+ Py_ssize_t thread_count = 0;
800+ _Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) {
801+ #ifdef PY_HAVE_THREAD_NATIVE_ID
802+ p->native_thread_id = tstate->native_thread_id;
803+ #endif
804+ if (p->_whence == _PyThreadState_WHENCE_THREADING
805+ || p->_whence == _PyThreadState_WHENCE_THREADING_DAEMON) {
806+ thread_count++;
807+ }
808+ }
809+ _Py_atomic_store_ssize(&interp->threads.count, thread_count);
810+ }
811+
812+ // Clearing thread states can call destructors, so resume before doing so.
813+ _PyEval_StartTheWorldAll(&_PyRuntime);
814+ _PyThreadState_DeleteList(states[0].deleted, /*is_after_fork=*/1);
815+ for (size_t i = 1; i < count; i++) {
816+ struct after_fork_state *state = &states[i];
817+ PyInterpreterState *interp = state->tstate->interp;
818+ PyThreadState_Swap(state->tstate);
819+ _PyThreadState_DeleteList(state->deleted, /*is_after_fork=*/1);
820+ run_at_forkers(interp->after_forkers_child, 0);
821+ _Py_atomic_store_ptr(&interp->threads.main, state->running_main);
822+ PyThreadState_Clear(state->tstate);
823+ PyThreadState_Swap(tstate);
824+ PyThreadState_Delete(state->tstate);
825+ }
826+ PyMem_RawFree(states);
827+ return _PyStatus_OK();
828+ }
829+
756830
757831void
758832PyOS_AfterFork_Child(void)
@@ -774,11 +848,6 @@ PyOS_AfterFork_Child(void)
774848 tstate->native_thread_id = PyThread_get_thread_native_id();
775849#endif
776850
777- #ifdef Py_GIL_DISABLED
778- _Py_brc_after_fork(tstate->interp);
779- _Py_qsbr_after_fork((_PyThreadStateImpl *)tstate);
780- #endif
781-
782851 // Ideally we could guarantee tstate is running main.
783852 _PyInterpreterState_ReinitRunningMain(tstate);
784853
@@ -794,32 +863,20 @@ PyOS_AfterFork_Child(void)
794863 _Py_jit_debug_mutex = (PyMutex){0};
795864#endif
796865
797- // Thread-state cleanup can run finalizers that issue warnings.
798- _PyWarnings_AfterFork(tstate->interp);
799-
800866 reset_remotedebug_data(tstate);
801867
802868 reset_asyncio_state((_PyThreadStateImpl *)tstate);
803869
804- // Remove the dead thread states. We "start the world" once we are the only
805- // thread state left to undo the stop the world call in `PyOS_BeforeFork`.
806- // That needs to happen before `_PyThreadState_DeleteList`, because that
807- // may call destructors.
808- PyThreadState *list = _PyThreadState_RemoveExcept(tstate);
809- _PyEval_StartTheWorldAll(&_PyRuntime);
810- _PyThreadState_DeleteList(list, /*is_after_fork=*/1);
870+ status = clear_threads_after_fork(tstate);
871+ if (_PyStatus_EXCEPTION(status)) {
872+ goto fatal_error;
873+ }
811874
812875 _PyImport_ReInitLock(tstate->interp);
813876 _PyImport_ReleaseLock(tstate->interp);
814877
815878 _PySignal_AfterFork();
816879
817- status = _PyInterpreterState_DeleteExceptMain(runtime);
818- if (_PyStatus_EXCEPTION(status)) {
819- goto fatal_error;
820- }
821- assert(_PyThreadState_GET() == tstate);
822-
823880 status = _PyPerfTrampoline_AfterFork_Child();
824881 if (_PyStatus_EXCEPTION(status)) {
825882 goto fatal_error;
0 commit comments