PyOS_AfterFork_Child() uses PyStatus (GH-20596)

PyOS_AfterFork_Child() helper functions now return a PyStatus:
PyOS_AfterFork_Child() is now responsible to handle errors.

* Move _PySignal_AfterFork() to the internal C API
* Add #ifdef HAVE_FORK on _PyGILState_Reinit(), _PySignal_AfterFork()
  and _PyInterpreterState_DeleteExceptMain().
This commit is contained in:
Victor Stinner 2020-06-02 15:51:37 +02:00 committed by GitHub
parent 297257f7bc
commit 26881c8fae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 84 additions and 50 deletions

View File

@ -25,7 +25,7 @@ PyAPI_FUNC(int) _PyEval_AddPendingCall(
void *arg); void *arg);
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyThreadState *tstate); PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyThreadState *tstate);
#ifdef HAVE_FORK #ifdef HAVE_FORK
extern void _PyEval_ReInitThreads(struct pyruntimestate *runtime); extern PyStatus _PyEval_ReInitThreads(struct pyruntimestate *runtime);
#endif #endif
PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth( PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth(
PyThreadState *tstate, PyThreadState *tstate,

View File

@ -11,7 +11,7 @@ PyAPI_FUNC(PyObject *) _PyImport_FindBuiltin(
); );
#ifdef HAVE_FORK #ifdef HAVE_FORK
extern void _PyImport_ReInitLock(void); extern PyStatus _PyImport_ReInitLock(void);
#endif #endif
extern void _PyImport_Cleanup(PyThreadState *tstate); extern void _PyImport_Cleanup(PyThreadState *tstate);

View File

@ -131,9 +131,12 @@ PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap(
PyThreadState *newts); PyThreadState *newts);
PyAPI_FUNC(PyStatus) _PyInterpreterState_Enable(_PyRuntimeState *runtime); PyAPI_FUNC(PyStatus) _PyInterpreterState_Enable(_PyRuntimeState *runtime);
PyAPI_FUNC(void) _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime);
PyAPI_FUNC(void) _PyGILState_Reinit(_PyRuntimeState *runtime); #ifdef HAVE_FORK
extern PyStatus _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime);
extern PyStatus _PyGILState_Reinit(_PyRuntimeState *runtime);
extern void _PySignal_AfterFork(void);
#endif
PyAPI_FUNC(int) _PyState_AddModule( PyAPI_FUNC(int) _PyState_AddModule(

View File

@ -120,7 +120,7 @@ PyAPI_FUNC(PyStatus) _PyRuntimeState_Init(_PyRuntimeState *runtime);
PyAPI_FUNC(void) _PyRuntimeState_Fini(_PyRuntimeState *runtime); PyAPI_FUNC(void) _PyRuntimeState_Fini(_PyRuntimeState *runtime);
#ifdef HAVE_FORK #ifdef HAVE_FORK
PyAPI_FUNC(void) _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime); extern PyStatus _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime);
#endif #endif
/* Initialize _PyRuntimeState. /* Initialize _PyRuntimeState.

View File

@ -1,4 +1,3 @@
#ifndef Py_INTRCHECK_H #ifndef Py_INTRCHECK_H
#define Py_INTRCHECK_H #define Py_INTRCHECK_H
#ifdef __cplusplus #ifdef __cplusplus
@ -19,7 +18,6 @@ Py_DEPRECATED(3.7) PyAPI_FUNC(void) PyOS_AfterFork(void);
#ifndef Py_LIMITED_API #ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyOS_IsMainThread(void); PyAPI_FUNC(int) _PyOS_IsMainThread(void);
PyAPI_FUNC(void) _PySignal_AfterFork(void);
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
/* windows.h is not included by Python.h so use void* instead of HANDLE */ /* windows.h is not included by Python.h so use void* instead of HANDLE */

View File

@ -34,6 +34,7 @@
#include "pycore_ceval.h" // _PyEval_ReInitThreads() #include "pycore_ceval.h" // _PyEval_ReInitThreads()
#include "pycore_import.h" // _PyImport_ReInitLock() #include "pycore_import.h" // _PyImport_ReInitLock()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
#include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "structmember.h" // PyMemberDef #include "structmember.h" // PyMemberDef
#ifndef MS_WINDOWS #ifndef MS_WINDOWS
@ -461,15 +462,41 @@ PyOS_AfterFork_Parent(void)
void void
PyOS_AfterFork_Child(void) PyOS_AfterFork_Child(void)
{ {
PyStatus status;
_PyRuntimeState *runtime = &_PyRuntime; _PyRuntimeState *runtime = &_PyRuntime;
_PyGILState_Reinit(runtime);
_PyEval_ReInitThreads(runtime); status = _PyGILState_Reinit(runtime);
_PyImport_ReInitLock(); if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
status = _PyEval_ReInitThreads(runtime);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
status = _PyImport_ReInitLock();
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
_PySignal_AfterFork(); _PySignal_AfterFork();
_PyRuntimeState_ReInitThreads(runtime);
_PyInterpreterState_DeleteExceptMain(runtime); status = _PyRuntimeState_ReInitThreads(runtime);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
status = _PyInterpreterState_DeleteExceptMain(runtime);
if (_PyStatus_EXCEPTION(status)) {
goto fatal_error;
}
run_at_forkers(_PyInterpreterState_GET()->after_forkers_child, 0); run_at_forkers(_PyInterpreterState_GET()->after_forkers_child, 0);
return;
fatal_error:
Py_ExitStatusException(status);
} }
static int static int

View File

@ -1796,14 +1796,17 @@ PyOS_InterruptOccurred(void)
return 1; return 1;
} }
#ifdef HAVE_FORK
static void static void
_clear_pending_signals(void) _clear_pending_signals(void)
{ {
int i; if (!_Py_atomic_load(&is_tripped)) {
if (!_Py_atomic_load(&is_tripped))
return; return;
}
_Py_atomic_store(&is_tripped, 0); _Py_atomic_store(&is_tripped, 0);
for (i = 1; i < NSIG; ++i) { for (int i = 1; i < NSIG; ++i) {
_Py_atomic_store_relaxed(&Handlers[i].tripped, 0); _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
} }
} }
@ -1816,6 +1819,8 @@ _PySignal_AfterFork(void)
* the interpreter had an opportunity to call the handlers. issue9535. */ * the interpreter had an opportunity to call the handlers. issue9535. */
_clear_pending_signals(); _clear_pending_signals();
} }
#endif /* HAVE_FORK */
int int
_PyOS_IsMainThread(void) _PyOS_IsMainThread(void)

View File

@ -433,11 +433,9 @@ PyEval_ReleaseThread(PyThreadState *tstate)
#ifdef HAVE_FORK #ifdef HAVE_FORK
/* This function is called from PyOS_AfterFork_Child to destroy all threads /* This function is called from PyOS_AfterFork_Child to destroy all threads
* which are not running in the child process, and clear internal locks which are not running in the child process, and clear internal locks
* which might be held by those threads. which might be held by those threads. */
*/ PyStatus
void
_PyEval_ReInitThreads(_PyRuntimeState *runtime) _PyEval_ReInitThreads(_PyRuntimeState *runtime)
{ {
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime); PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
@ -449,7 +447,7 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
struct _gil_runtime_state *gil = &runtime->ceval.gil; struct _gil_runtime_state *gil = &runtime->ceval.gil;
#endif #endif
if (!gil_created(gil)) { if (!gil_created(gil)) {
return; return _PyStatus_OK();
} }
recreate_gil(gil); recreate_gil(gil);
@ -457,11 +455,12 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
struct _pending_calls *pending = &tstate->interp->ceval.pending; struct _pending_calls *pending = &tstate->interp->ceval.pending;
if (_PyThread_at_fork_reinit(&pending->lock) < 0) { if (_PyThread_at_fork_reinit(&pending->lock) < 0) {
Py_FatalError("Can't initialize threads for pending calls"); return _PyStatus_ERR("Can't reinitialize pending calls lock");
} }
/* Destroy all threads except the current one */ /* Destroy all threads except the current one */
_PyThreadState_DeleteExcept(runtime, tstate); _PyThreadState_DeleteExcept(runtime, tstate);
return _PyStatus_OK();
} }
#endif #endif

View File

@ -148,7 +148,7 @@ _PyImportZip_Init(PyThreadState *tstate)
in different threads to return with a partially loaded module. in different threads to return with a partially loaded module.
These calls are serialized by the global interpreter lock. */ These calls are serialized by the global interpreter lock. */
static PyThread_type_lock import_lock = 0; static PyThread_type_lock import_lock = NULL;
static unsigned long import_lock_thread = PYTHREAD_INVALID_THREAD_ID; static unsigned long import_lock_thread = PYTHREAD_INVALID_THREAD_ID;
static int import_lock_level = 0; static int import_lock_level = 0;
@ -171,7 +171,7 @@ _PyImport_AcquireLock(void)
!PyThread_acquire_lock(import_lock, 0)) !PyThread_acquire_lock(import_lock, 0))
{ {
PyThreadState *tstate = PyEval_SaveThread(); PyThreadState *tstate = PyEval_SaveThread();
PyThread_acquire_lock(import_lock, 1); PyThread_acquire_lock(import_lock, WAIT_LOCK);
PyEval_RestoreThread(tstate); PyEval_RestoreThread(tstate);
} }
assert(import_lock_level == 0); assert(import_lock_level == 0);
@ -197,19 +197,19 @@ _PyImport_ReleaseLock(void)
} }
#ifdef HAVE_FORK #ifdef HAVE_FORK
/* This function is called from PyOS_AfterFork_Child to ensure that newly /* This function is called from PyOS_AfterFork_Child() to ensure that newly
created child processes do not share locks with the parent. created child processes do not share locks with the parent.
We now acquire the import lock around fork() calls but on some platforms We now acquire the import lock around fork() calls but on some platforms
(Solaris 9 and earlier? see isue7242) that still left us with problems. */ (Solaris 9 and earlier? see isue7242) that still left us with problems. */
PyStatus
void
_PyImport_ReInitLock(void) _PyImport_ReInitLock(void)
{ {
if (import_lock != NULL) { if (import_lock != NULL) {
if (_PyThread_at_fork_reinit(&import_lock) < 0) { if (_PyThread_at_fork_reinit(&import_lock) < 0) {
_Py_FatalErrorFunc(__func__, "failed to create a new lock"); return _PyStatus_ERR("failed to create a new lock");
} }
} }
if (import_lock_level > 1) { if (import_lock_level > 1) {
/* Forked as a side effect of import */ /* Forked as a side effect of import */
unsigned long me = PyThread_get_thread_ident(); unsigned long me = PyThread_get_thread_ident();
@ -224,6 +224,7 @@ _PyImport_ReInitLock(void)
import_lock_thread = PYTHREAD_INVALID_THREAD_ID; import_lock_thread = PYTHREAD_INVALID_THREAD_ID;
import_lock_level = 0; import_lock_level = 0;
} }
return _PyStatus_OK();
} }
#endif #endif

View File

@ -124,10 +124,8 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)
#ifdef HAVE_FORK #ifdef HAVE_FORK
/* This function is called from PyOS_AfterFork_Child to ensure that /* This function is called from PyOS_AfterFork_Child to ensure that
* newly created child processes do not share locks with the parent. newly created child processes do not share locks with the parent. */
*/ PyStatus
void
_PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime) _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
{ {
// This was initially set in _PyRuntimeState_Init(). // This was initially set in _PyRuntimeState_Init().
@ -138,23 +136,20 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
PyMemAllocatorEx old_alloc; PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
int interp_mutex = _PyThread_at_fork_reinit(&runtime->interpreters.mutex); int reinit_interp = _PyThread_at_fork_reinit(&runtime->interpreters.mutex);
int main_interp_id_mutex = _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex); int reinit_main_id = _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex);
int xidregistry_mutex = _PyThread_at_fork_reinit(&runtime->xidregistry.mutex); int reinit_xidregistry = _PyThread_at_fork_reinit(&runtime->xidregistry.mutex);
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (interp_mutex < 0) { if (reinit_interp < 0
Py_FatalError("Can't initialize lock for runtime interpreters"); || reinit_main_id < 0
} || reinit_xidregistry < 0)
{
return _PyStatus_ERR("Failed to reinitialize runtime locks");
if (main_interp_id_mutex < 0) {
Py_FatalError("Can't initialize ID lock for main interpreter");
}
if (xidregistry_mutex < 0) {
Py_FatalError("Can't initialize lock for cross-interpreter data registry");
} }
return _PyStatus_OK();
} }
#endif #endif
@ -373,11 +368,12 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
} }
#ifdef HAVE_FORK
/* /*
* Delete all interpreter states except the main interpreter. If there * Delete all interpreter states except the main interpreter. If there
* is a current interpreter state, it *must* be the main interpreter. * is a current interpreter state, it *must* be the main interpreter.
*/ */
void PyStatus
_PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime) _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
{ {
struct _gilstate_runtime_state *gilstate = &runtime->gilstate; struct _gilstate_runtime_state *gilstate = &runtime->gilstate;
@ -385,7 +381,7 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
PyThreadState *tstate = _PyThreadState_Swap(gilstate, NULL); PyThreadState *tstate = _PyThreadState_Swap(gilstate, NULL);
if (tstate != NULL && tstate->interp != interpreters->main) { if (tstate != NULL && tstate->interp != interpreters->main) {
Py_FatalError("not main interpreter"); return _PyStatus_ERR("not main interpreter");
} }
HEAD_LOCK(runtime); HEAD_LOCK(runtime);
@ -411,10 +407,12 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
HEAD_UNLOCK(runtime); HEAD_UNLOCK(runtime);
if (interpreters->head == NULL) { if (interpreters->head == NULL) {
Py_FatalError("missing main interpreter"); return _PyStatus_ERR("missing main interpreter");
} }
_PyThreadState_Swap(gilstate, tstate); _PyThreadState_Swap(gilstate, tstate);
return _PyStatus_OK();
} }
#endif
PyInterpreterState * PyInterpreterState *
@ -1259,11 +1257,12 @@ _PyGILState_Fini(PyThreadState *tstate)
gilstate->autoInterpreterState = NULL; gilstate->autoInterpreterState = NULL;
} }
#ifdef HAVE_FORK
/* Reset the TSS key - called by PyOS_AfterFork_Child(). /* Reset the TSS key - called by PyOS_AfterFork_Child().
* This should not be necessary, but some - buggy - pthread implementations * This should not be necessary, but some - buggy - pthread implementations
* don't reset TSS upon fork(), see issue #10517. * don't reset TSS upon fork(), see issue #10517.
*/ */
void PyStatus
_PyGILState_Reinit(_PyRuntimeState *runtime) _PyGILState_Reinit(_PyRuntimeState *runtime)
{ {
struct _gilstate_runtime_state *gilstate = &runtime->gilstate; struct _gilstate_runtime_state *gilstate = &runtime->gilstate;
@ -1271,7 +1270,7 @@ _PyGILState_Reinit(_PyRuntimeState *runtime)
PyThread_tss_delete(&gilstate->autoTSSkey); PyThread_tss_delete(&gilstate->autoTSSkey);
if (PyThread_tss_create(&gilstate->autoTSSkey) != 0) { if (PyThread_tss_create(&gilstate->autoTSSkey) != 0) {
Py_FatalError("Could not allocate TSS entry"); return _PyStatus_NO_MEMORY();
} }
/* If the thread had an associated auto thread state, reassociate it with /* If the thread had an associated auto thread state, reassociate it with
@ -1279,9 +1278,11 @@ _PyGILState_Reinit(_PyRuntimeState *runtime)
if (tstate && if (tstate &&
PyThread_tss_set(&gilstate->autoTSSkey, (void *)tstate) != 0) PyThread_tss_set(&gilstate->autoTSSkey, (void *)tstate) != 0)
{ {
Py_FatalError("Couldn't create autoTSSkey mapping"); return _PyStatus_ERR("failed to set autoTSSkey");
} }
return _PyStatus_OK();
} }
#endif
/* When a thread state is created for a thread by some mechanism other than /* When a thread state is created for a thread by some mechanism other than
PyGILState_Ensure, it's important that the GILState machinery knows about PyGILState_Ensure, it's important that the GILState machinery knows about