Issue #17094: Clear stale thread states after fork().

Note that this is a potentially disruptive change since it may
release some system resources which would otherwise remain
perpetually alive (e.g. database connections kept in thread-local
storage).
This commit is contained in:
Antoine Pitrou 2013-05-05 23:47:09 +02:00
parent 39b17c513a
commit 8408cea0cd
5 changed files with 88 additions and 8 deletions

View file

@ -362,29 +362,28 @@ PyEval_ReleaseThread(PyThreadState *tstate)
drop_gil(tstate);
}
/* This function is called from PyOS_AfterFork to ensure that newly
created child processes don't hold locks referring to threads which
are not running in the child process. (This could also be done using
pthread_atfork mechanism, at least for the pthreads implementation.) */
/* This function is called from PyOS_AfterFork to destroy all threads which are
* not running in the child process, and clear internal locks which might be
* held by those threads. (This could also be done using pthread_atfork
* mechanism, at least for the pthreads implementation.) */
void
PyEval_ReInitThreads(void)
{
_Py_IDENTIFIER(_after_fork);
PyObject *threading, *result;
PyThreadState *tstate = PyThreadState_GET();
PyThreadState *current_tstate = PyThreadState_GET();
if (!gil_created())
return;
recreate_gil();
pending_lock = PyThread_allocate_lock();
take_gil(tstate);
take_gil(current_tstate);
main_thread = PyThread_get_thread_ident();
/* Update the threading module with the new state.
*/
tstate = PyThreadState_GET();
threading = PyMapping_GetItemString(tstate->interp->modules,
threading = PyMapping_GetItemString(current_tstate->interp->modules,
"threading");
if (threading == NULL) {
/* threading not imported */
@ -397,6 +396,9 @@ PyEval_ReInitThreads(void)
else
Py_DECREF(result);
Py_DECREF(threading);
/* Destroy all threads except the current one */
_PyThreadState_DeleteExcept(current_tstate);
}
#else

View file

@ -414,6 +414,53 @@ PyThreadState_DeleteCurrent()
#endif /* WITH_THREAD */
/*
* Delete all thread states except the one passed as argument.
* Note that, if there is a current thread state, it *must* be the one
* passed as argument. Also, this won't touch any other interpreters
* than the current one, since we don't know which thread state should
* be kept in those other interpreteres.
*/
void
_PyThreadState_DeleteExcept(PyThreadState *tstate)
{
PyInterpreterState *interp = tstate->interp;
PyThreadState *p, *next, *garbage;
HEAD_LOCK();
/* Remove all thread states, except tstate, from the linked list of
thread states. This will allow calling PyThreadState_Clear()
without holding the lock.
XXX This would be simpler with a doubly-linked list. */
garbage = interp->tstate_head;
interp->tstate_head = tstate;
if (garbage == tstate) {
garbage = garbage->next;
tstate->next = NULL;
}
else {
for (p = garbage; p; p = p->next) {
if (p->next == tstate) {
p->next = tstate->next;
tstate->next = NULL;
break;
}
}
}
if (tstate->next != NULL)
Py_FatalError("_PyThreadState_DeleteExcept: tstate not found "
"in interpreter thread states");
HEAD_UNLOCK();
/* 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. */
for (p = garbage; p; p = next) {
next = p->next;
PyThreadState_Clear(p);
free(p);
}
}
PyThreadState *
PyThreadState_Get(void)
{