run_at_forkers(interp->before_forkers, 1);
_PyImport_AcquireLock(interp);
+ _PyEval_StopTheWorldAll(&_PyRuntime);
+ HEAD_LOCK(&_PyRuntime);
}
void
PyOS_AfterFork_Parent(void)
{
+ HEAD_UNLOCK(&_PyRuntime);
+ _PyEval_StartTheWorldAll(&_PyRuntime);
+
PyInterpreterState *interp = _PyInterpreterState_GET();
if (_PyImport_ReleaseLock(interp) <= 0) {
Py_FatalError("failed releasing import lock after fork");
PyStatus status;
_PyRuntimeState *runtime = &_PyRuntime;
+ // re-creates runtime->interpreters.mutex (HEAD_UNLOCK)
status = _PyRuntimeState_ReInitThreads(runtime);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
// running in the process. Best effort, silent if unable to count threads.
// Constraint: Quick. Never overcounts. Never leaves an error set.
//
-// This code might do an import, thus acquiring the import lock, which
-// PyOS_BeforeFork() also does. As this should only be called from
-// the parent process, it is in the same thread so that works.
-static void warn_about_fork_with_threads(const char* name) {
+// This should only be called from the parent process after
+// PyOS_AfterFork_Parent().
+static void
+warn_about_fork_with_threads(const char* name)
+{
+ // It's not safe to issue the warning while the world is stopped, because
+ // other threads might be holding locks that we need, which would deadlock.
+ assert(!_PyRuntime.stoptheworld.world_stopped);
+
// TODO: Consider making an `os` module API to return the current number
// of threads in the process. That'd presumably use this platform code but
// raise an error rather than using the inaccurate fallback.
/* child: this clobbers and resets the import lock. */
PyOS_AfterFork_Child();
} else {
- warn_about_fork_with_threads("fork1");
/* parent: release the import lock. */
PyOS_AfterFork_Parent();
+ // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
+ warn_about_fork_with_threads("fork1");
}
if (pid == -1) {
errno = saved_errno;
/* child: this clobbers and resets the import lock. */
PyOS_AfterFork_Child();
} else {
- warn_about_fork_with_threads("fork");
/* parent: release the import lock. */
PyOS_AfterFork_Parent();
+ // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
+ warn_about_fork_with_threads("fork");
}
if (pid == -1) {
errno = saved_errno;
/* child: this clobbers and resets the import lock. */
PyOS_AfterFork_Child();
} else {
- warn_about_fork_with_threads("forkpty");
/* parent: release the import lock. */
PyOS_AfterFork_Parent();
+ // After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
+ warn_about_fork_with_threads("forkpty");
}
if (pid == -1) {
return posix_error();
PyInterpreterState *interp = tstate->interp;
_PyRuntimeState *runtime = interp->runtime;
+#ifdef Py_GIL_DISABLED
+ assert(runtime->stoptheworld.world_stopped);
+#endif
+
HEAD_LOCK(runtime);
/* Remove all thread states, except tstate, from the linked list of
thread states. This will allow calling PyThreadState_Clear()
interp->threads.head = tstate;
HEAD_UNLOCK(runtime);
+ _PyEval_StartTheWorldAll(runtime);
+
/* Clear and deallocate all stale thread states. Even if this
executes Python code, we should be safe since it executes
in the current thread, not one of the stale threads. */