Skip to content

PyOS_AfterFork_Child() uses PyStatus #20596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ PyAPI_FUNC(int) _PyEval_AddPendingCall(
void *arg);
PyAPI_FUNC(void) _PyEval_SignalAsyncExc(PyThreadState *tstate);
#ifdef HAVE_FORK
extern void _PyEval_ReInitThreads(struct pyruntimestate *runtime);
extern PyStatus _PyEval_ReInitThreads(struct pyruntimestate *runtime);
#endif
PyAPI_FUNC(void) _PyEval_SetCoroutineOriginTrackingDepth(
PyThreadState *tstate,
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_import.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PyAPI_FUNC(PyObject *) _PyImport_FindBuiltin(
);

#ifdef HAVE_FORK
extern void _PyImport_ReInitLock(void);
extern PyStatus _PyImport_ReInitLock(void);
#endif
extern void _PyImport_Cleanup(PyThreadState *tstate);

Expand Down
7 changes: 5 additions & 2 deletions Include/internal/pycore_pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,12 @@ PyAPI_FUNC(PyThreadState *) _PyThreadState_Swap(
PyThreadState *newts);

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(
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ PyAPI_FUNC(PyStatus) _PyRuntimeState_Init(_PyRuntimeState *runtime);
PyAPI_FUNC(void) _PyRuntimeState_Fini(_PyRuntimeState *runtime);

#ifdef HAVE_FORK
PyAPI_FUNC(void) _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime);
extern PyStatus _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime);
#endif

/* Initialize _PyRuntimeState.
Expand Down
2 changes: 0 additions & 2 deletions Include/intrcheck.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#ifndef Py_INTRCHECK_H
#define Py_INTRCHECK_H
#ifdef __cplusplus
Expand All @@ -19,7 +18,6 @@ Py_DEPRECATED(3.7) PyAPI_FUNC(void) PyOS_AfterFork(void);

#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyOS_IsMainThread(void);
PyAPI_FUNC(void) _PySignal_AfterFork(void);

#ifdef MS_WINDOWS
/* windows.h is not included by Python.h so use void* instead of HANDLE */
Expand Down
37 changes: 32 additions & 5 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include "pycore_ceval.h" // _PyEval_ReInitThreads()
#include "pycore_import.h" // _PyImport_ReInitLock()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "structmember.h" // PyMemberDef
#ifndef MS_WINDOWS
Expand Down Expand Up @@ -461,15 +462,41 @@ PyOS_AfterFork_Parent(void)
void
PyOS_AfterFork_Child(void)
{
PyStatus status;
_PyRuntimeState *runtime = &_PyRuntime;
_PyGILState_Reinit(runtime);
_PyEval_ReInitThreads(runtime);
_PyImport_ReInitLock();

status = _PyGILState_Reinit(runtime);
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();
_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);
return;

fatal_error:
Py_ExitStatusException(status);
}

static int
Expand Down
11 changes: 8 additions & 3 deletions Modules/signalmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1796,14 +1796,17 @@ PyOS_InterruptOccurred(void)
return 1;
}


#ifdef HAVE_FORK
static void
_clear_pending_signals(void)
{
int i;
if (!_Py_atomic_load(&is_tripped))
if (!_Py_atomic_load(&is_tripped)) {
return;
}

_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);
}
}
Expand All @@ -1816,6 +1819,8 @@ _PySignal_AfterFork(void)
* the interpreter had an opportunity to call the handlers. issue9535. */
_clear_pending_signals();
}
#endif /* HAVE_FORK */


int
_PyOS_IsMainThread(void)
Expand Down
13 changes: 6 additions & 7 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,9 @@ PyEval_ReleaseThread(PyThreadState *tstate)

#ifdef HAVE_FORK
/* 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 might be held by those threads.
*/

void
which are not running in the child process, and clear internal locks
which might be held by those threads. */
PyStatus
_PyEval_ReInitThreads(_PyRuntimeState *runtime)
{
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
Expand All @@ -449,19 +447,20 @@ _PyEval_ReInitThreads(_PyRuntimeState *runtime)
struct _gil_runtime_state *gil = &runtime->ceval.gil;
#endif
if (!gil_created(gil)) {
return;
return _PyStatus_OK();
}
recreate_gil(gil);

take_gil(tstate);

struct _pending_calls *pending = &tstate->interp->ceval.pending;
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 */
_PyThreadState_DeleteExcept(runtime, tstate);
return _PyStatus_OK();
}
#endif

Expand Down
13 changes: 7 additions & 6 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ _PyImportZip_Init(PyThreadState *tstate)
in different threads to return with a partially loaded module.
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 int import_lock_level = 0;

Expand All @@ -171,7 +171,7 @@ _PyImport_AcquireLock(void)
!PyThread_acquire_lock(import_lock, 0))
{
PyThreadState *tstate = PyEval_SaveThread();
PyThread_acquire_lock(import_lock, 1);
PyThread_acquire_lock(import_lock, WAIT_LOCK);
PyEval_RestoreThread(tstate);
}
assert(import_lock_level == 0);
Expand All @@ -197,19 +197,19 @@ _PyImport_ReleaseLock(void)
}

#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.
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. */

void
PyStatus
_PyImport_ReInitLock(void)
{
if (import_lock != NULL) {
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) {
/* Forked as a side effect of import */
unsigned long me = PyThread_get_thread_ident();
Expand All @@ -224,6 +224,7 @@ _PyImport_ReInitLock(void)
import_lock_thread = PYTHREAD_INVALID_THREAD_ID;
import_lock_level = 0;
}
return _PyStatus_OK();
}
#endif

Expand Down
45 changes: 23 additions & 22 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,8 @@ _PyRuntimeState_Fini(_PyRuntimeState *runtime)

#ifdef HAVE_FORK
/* This function is called from PyOS_AfterFork_Child to ensure that
* newly created child processes do not share locks with the parent.
*/

void
newly created child processes do not share locks with the parent. */
PyStatus
_PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
{
// This was initially set in _PyRuntimeState_Init().
Expand All @@ -138,23 +136,20 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

int interp_mutex = _PyThread_at_fork_reinit(&runtime->interpreters.mutex);
int main_interp_id_mutex = _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex);
int xidregistry_mutex = _PyThread_at_fork_reinit(&runtime->xidregistry.mutex);
int reinit_interp = _PyThread_at_fork_reinit(&runtime->interpreters.mutex);
int reinit_main_id = _PyThread_at_fork_reinit(&runtime->interpreters.main->id_mutex);
int reinit_xidregistry = _PyThread_at_fork_reinit(&runtime->xidregistry.mutex);

PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);

if (interp_mutex < 0) {
Py_FatalError("Can't initialize lock for runtime interpreters");
}

if (main_interp_id_mutex < 0) {
Py_FatalError("Can't initialize ID lock for main interpreter");
}
if (reinit_interp < 0
|| reinit_main_id < 0
|| reinit_xidregistry < 0)
{
return _PyStatus_ERR("Failed to reinitialize runtime locks");

if (xidregistry_mutex < 0) {
Py_FatalError("Can't initialize lock for cross-interpreter data registry");
}
return _PyStatus_OK();
}
#endif

Expand Down Expand Up @@ -373,19 +368,20 @@ PyInterpreterState_Delete(PyInterpreterState *interp)
}


#ifdef HAVE_FORK
/*
* Delete all interpreter states except the main interpreter. If there
* is a current interpreter state, it *must* be the main interpreter.
*/
void
PyStatus
_PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
{
struct _gilstate_runtime_state *gilstate = &runtime->gilstate;
struct pyinterpreters *interpreters = &runtime->interpreters;

PyThreadState *tstate = _PyThreadState_Swap(gilstate, NULL);
if (tstate != NULL && tstate->interp != interpreters->main) {
Py_FatalError("not main interpreter");
return _PyStatus_ERR("not main interpreter");
}

HEAD_LOCK(runtime);
Expand All @@ -411,10 +407,12 @@ _PyInterpreterState_DeleteExceptMain(_PyRuntimeState *runtime)
HEAD_UNLOCK(runtime);

if (interpreters->head == NULL) {
Py_FatalError("missing main interpreter");
return _PyStatus_ERR("missing main interpreter");
}
_PyThreadState_Swap(gilstate, tstate);
return _PyStatus_OK();
}
#endif


PyInterpreterState *
Expand Down Expand Up @@ -1259,29 +1257,32 @@ _PyGILState_Fini(PyThreadState *tstate)
gilstate->autoInterpreterState = NULL;
}

#ifdef HAVE_FORK
/* Reset the TSS key - called by PyOS_AfterFork_Child().
* This should not be necessary, but some - buggy - pthread implementations
* don't reset TSS upon fork(), see issue #10517.
*/
void
PyStatus
_PyGILState_Reinit(_PyRuntimeState *runtime)
{
struct _gilstate_runtime_state *gilstate = &runtime->gilstate;
PyThreadState *tstate = _PyGILState_GetThisThreadState(gilstate);

PyThread_tss_delete(&gilstate->autoTSSkey);
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
* the new key. */
if (tstate &&
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
PyGILState_Ensure, it's important that the GILState machinery knows about
Expand Down