From 8dcde94c6068d7bb9c006091217617e2c11f75a9 Mon Sep 17 00:00:00 2001 From: Kristj?n Valur J?nsson Date: Sat, 23 Mar 2013 03:36:16 -0700 Subject: Issue #17522: Add the PyGILState_Check() API. --- Python/pystate.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index cfd61d0098..70038936d6 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -697,6 +697,15 @@ PyGILState_GetThisThreadState(void) return (PyThreadState *)PyThread_get_key_value(autoTLSkey); } +int +PyGILState_Check(void) +{ + /* can't use PyThreadState_Get() since it will assert that it has the GIL */ + PyThreadState *tstate = (PyThreadState*)_Py_atomic_load_relaxed( + &_PyThreadState_Current); + return tstate && (tstate == PyGILState_GetThisThreadState()); +} + PyGILState_STATE PyGILState_Ensure(void) { -- cgit v1.2.1 From 3c24040c4c2cb747153c3480cae23babebe6457d Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 5 May 2013 23:47:09 +0200 Subject: 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). --- Python/pystate.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 70038936d6..2a6f16c87f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -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) { -- cgit v1.2.1 From 888977e55b0f7b8a0827956da732e7e099b6c308 Mon Sep 17 00:00:00 2001 From: Charles-Francois Natali Date: Wed, 8 May 2013 21:09:52 +0200 Subject: Issue #17912: Use a doubly linked-list for thread states. --- Python/pystate.c | 58 +++++++++++++++++--------------------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 2a6f16c87f..a8ed13867b 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -213,7 +213,10 @@ new_threadstate(PyInterpreterState *interp, int init) _PyThreadState_Init(tstate); HEAD_LOCK(); + tstate->prev = NULL; tstate->next = interp->tstate_head; + if (tstate->next) + tstate->next->prev = tstate; interp->tstate_head = tstate; HEAD_UNLOCK(); } @@ -349,35 +352,18 @@ static void tstate_delete_common(PyThreadState *tstate) { PyInterpreterState *interp; - PyThreadState **p; - PyThreadState *prev_p = NULL; if (tstate == NULL) Py_FatalError("PyThreadState_Delete: NULL tstate"); interp = tstate->interp; if (interp == NULL) Py_FatalError("PyThreadState_Delete: NULL interp"); HEAD_LOCK(); - for (p = &interp->tstate_head; ; p = &(*p)->next) { - if (*p == NULL) - Py_FatalError( - "PyThreadState_Delete: invalid tstate"); - if (*p == tstate) - break; - /* Sanity check. These states should never happen but if - * they do we must abort. Otherwise we'll end up spinning in - * in a tight loop with the lock held. A similar check is done - * in thread.c find_key(). */ - if (*p == prev_p) - Py_FatalError( - "PyThreadState_Delete: small circular list(!)" - " and tstate not found."); - prev_p = *p; - if ((*p)->next == interp->tstate_head) - Py_FatalError( - "PyThreadState_Delete: circular list(!) and" - " tstate not found."); - } - *p = tstate->next; + if (tstate->prev) + tstate->prev->next = tstate->next; + else + interp->tstate_head = tstate->next; + if (tstate->next) + tstate->next->prev = tstate->prev; HEAD_UNLOCK(); free(tstate); } @@ -429,26 +415,16 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate) 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. */ + without holding the lock. */ garbage = interp->tstate_head; + if (garbage == tstate) + garbage = tstate->next; + if (tstate->prev) + tstate->prev->next = tstate->next; + if (tstate->next) + tstate->next->prev = tstate->prev; + tstate->prev = tstate->next = NULL; 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 -- cgit v1.2.1 From 0e6215070ac12c5aecdce573f75624eaa7ed056e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 7 Jul 2013 16:25:15 +0200 Subject: Issue #18203: Replace malloc() with PyMem_RawMalloc() at Python initialization * Replace malloc() with PyMem_RawMalloc() * Replace PyMem_Malloc() with PyMem_RawMalloc() where the GIL is not held. * _Py_char2wchar() now returns a buffer allocated by PyMem_RawMalloc(), instead of PyMem_Malloc() --- Python/pystate.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 6609ee99ec..40606bf1ca 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -6,11 +6,11 @@ /* -------------------------------------------------------------------------- CAUTION -Always use malloc() and free() directly in this file. A number of these -functions are advertised as safe to call when the GIL isn't held, and in -a debug build Python redirects (e.g.) PyMem_NEW (etc) to Python's debugging -obmalloc functions. Those aren't thread-safe (they rely on the GIL to avoid -the expense of doing their own locking). +Always use PyMem_RawMalloc() and PyMem_RawFree() directly in this file. A +number of these functions are advertised as safe to call when the GIL isn't +held, and in a debug build Python redirects (e.g.) PyMem_NEW (etc) to Python's +debugging obmalloc functions. Those aren't thread-safe (they rely on the GIL +to avoid the expense of doing their own locking). -------------------------------------------------------------------------- */ #ifdef HAVE_DLOPEN @@ -60,7 +60,7 @@ PyInterpreterState * PyInterpreterState_New(void) { PyInterpreterState *interp = (PyInterpreterState *) - malloc(sizeof(PyInterpreterState)); + PyMem_RawMalloc(sizeof(PyInterpreterState)); if (interp != NULL) { HEAD_INIT(); @@ -148,7 +148,7 @@ PyInterpreterState_Delete(PyInterpreterState *interp) Py_FatalError("PyInterpreterState_Delete: remaining threads"); *p = interp->next; HEAD_UNLOCK(); - free(interp); + PyMem_RawFree(interp); #ifdef WITH_THREAD if (interp_head == NULL && head_mutex != NULL) { PyThread_free_lock(head_mutex); @@ -168,7 +168,7 @@ threadstate_getframe(PyThreadState *self) static PyThreadState * new_threadstate(PyInterpreterState *interp, int init) { - PyThreadState *tstate = (PyThreadState *)malloc(sizeof(PyThreadState)); + PyThreadState *tstate = (PyThreadState *)PyMem_RawMalloc(sizeof(PyThreadState)); if (_PyThreadState_GetFrame == NULL) _PyThreadState_GetFrame = threadstate_getframe; @@ -365,7 +365,7 @@ tstate_delete_common(PyThreadState *tstate) if (tstate->next) tstate->next->prev = tstate->prev; HEAD_UNLOCK(); - free(tstate); + PyMem_RawFree(tstate); } @@ -432,7 +432,7 @@ _PyThreadState_DeleteExcept(PyThreadState *tstate) for (p = garbage; p; p = next) { next = p->next; PyThreadState_Clear(p); - free(p); + PyMem_RawFree(p); } } -- cgit v1.2.1 From 1406c9e41bf56b4ec28c417b2f79abf5c26c77b9 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 1 Aug 2013 22:07:06 +0200 Subject: Issue #10241: Clear extension module dict copies at interpreter shutdown. Patch by Neil Schemenauer, minimally modified. --- Python/pystate.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 40606bf1ca..924b6a2909 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -320,6 +320,31 @@ PyState_RemoveModule(struct PyModuleDef* def) return PyList_SetItem(state->modules_by_index, index, Py_None); } +/* used by import.c:PyImport_Cleanup */ +void +_PyState_ClearModules(void) +{ + PyInterpreterState *state = PyThreadState_GET()->interp; + if (state->modules_by_index) { + Py_ssize_t i; + for (i = 0; i < PyList_GET_SIZE(state->modules_by_index); i++) { + PyObject *m = PyList_GET_ITEM(state->modules_by_index, i); + if (PyModule_Check(m)) { + /* cleanup the saved copy of module dicts */ + PyModuleDef *md = PyModule_GetDef(m); + if (md) + Py_CLEAR(md->m_base.m_copy); + } + } + /* Setting modules_by_index to NULL could be dangerous, so we + clear the list instead. */ + if (PyList_SetSlice(state->modules_by_index, + 0, PyList_GET_SIZE(state->modules_by_index), + NULL)) + PyErr_WriteUnraisable(state->modules_by_index); + } +} + void PyThreadState_Clear(PyThreadState *tstate) { -- cgit v1.2.1 From 5d62cd8fe1ac1e2f8286f286530bf16e995cd34d Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 2 Aug 2013 20:39:46 +0200 Subject: Backout 62658d9d8926 (issue #10241): it causes a crash at shutdown when deallocating a Tkapp object. --- Python/pystate.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 924b6a2909..40606bf1ca 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -320,31 +320,6 @@ PyState_RemoveModule(struct PyModuleDef* def) return PyList_SetItem(state->modules_by_index, index, Py_None); } -/* used by import.c:PyImport_Cleanup */ -void -_PyState_ClearModules(void) -{ - PyInterpreterState *state = PyThreadState_GET()->interp; - if (state->modules_by_index) { - Py_ssize_t i; - for (i = 0; i < PyList_GET_SIZE(state->modules_by_index); i++) { - PyObject *m = PyList_GET_ITEM(state->modules_by_index, i); - if (PyModule_Check(m)) { - /* cleanup the saved copy of module dicts */ - PyModuleDef *md = PyModule_GetDef(m); - if (md) - Py_CLEAR(md->m_base.m_copy); - } - } - /* Setting modules_by_index to NULL could be dangerous, so we - clear the list instead. */ - if (PyList_SetSlice(state->modules_by_index, - 0, PyList_GET_SIZE(state->modules_by_index), - NULL)) - PyErr_WriteUnraisable(state->modules_by_index); - } -} - void PyThreadState_Clear(PyThreadState *tstate) { -- cgit v1.2.1 From e4564245bddda91df68f13f211e1583ecf6f8c1a Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 11 Aug 2013 00:30:09 +0200 Subject: Issue #10241: Clear extension module dict copies at interpreter shutdown. Patch by Neil Schemenauer, minimally modified. (re-apply after fix for tkinter-related crash) --- Python/pystate.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 40606bf1ca..924b6a2909 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -320,6 +320,31 @@ PyState_RemoveModule(struct PyModuleDef* def) return PyList_SetItem(state->modules_by_index, index, Py_None); } +/* used by import.c:PyImport_Cleanup */ +void +_PyState_ClearModules(void) +{ + PyInterpreterState *state = PyThreadState_GET()->interp; + if (state->modules_by_index) { + Py_ssize_t i; + for (i = 0; i < PyList_GET_SIZE(state->modules_by_index); i++) { + PyObject *m = PyList_GET_ITEM(state->modules_by_index, i); + if (PyModule_Check(m)) { + /* cleanup the saved copy of module dicts */ + PyModuleDef *md = PyModule_GetDef(m); + if (md) + Py_CLEAR(md->m_base.m_copy); + } + } + /* Setting modules_by_index to NULL could be dangerous, so we + clear the list instead. */ + if (PyList_SetSlice(state->modules_by_index, + 0, PyList_GET_SIZE(state->modules_by_index), + NULL)) + PyErr_WriteUnraisable(state->modules_by_index); + } +} + void PyThreadState_Clear(PyThreadState *tstate) { -- cgit v1.2.1 From f19573c64e5f7cd47f9fe4028f3e5e8b4035530b Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 7 Sep 2013 23:38:37 +0200 Subject: Issue #18808: Thread.join() now waits for the underlying thread state to be destroyed before returning. This prevents unpredictable aborts in Py_EndInterpreter() when some non-daemon threads are still running. --- Python/pystate.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 924b6a2909..ecd00ce5b4 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -208,6 +208,8 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->trash_delete_nesting = 0; tstate->trash_delete_later = NULL; + tstate->on_delete = NULL; + tstate->on_delete_data = NULL; if (init) _PyThreadState_Init(tstate); @@ -390,6 +392,9 @@ tstate_delete_common(PyThreadState *tstate) if (tstate->next) tstate->next->prev = tstate->prev; HEAD_UNLOCK(); + if (tstate->on_delete != NULL) { + tstate->on_delete(tstate->on_delete_data); + } PyMem_RawFree(tstate); } -- cgit v1.2.1 From 26c734b2661005bc05d2ec901735a66971cc14e5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 9 Oct 2013 14:53:01 +0200 Subject: Close #19199: Remove ``PyThreadState.tick_counter`` field --- Python/pystate.c | 1 - 1 file changed, 1 deletion(-) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index ecd00ce5b4..6be71de2ae 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -182,7 +182,6 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->recursion_critical = 0; tstate->tracing = 0; tstate->use_tracing = 0; - tstate->tick_counter = 0; tstate->gilstate_counter = 0; tstate->async_exc = NULL; #ifdef WITH_THREAD -- cgit v1.2.1 From 8a61f72f3b16ffad9a22d885bc929a4f70630f0a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 13 Dec 2013 01:46:43 +0100 Subject: Close #19576: PyGILState_Ensure() now initializes threads. At startup, Python has no concrete GIL. If PyGILState_Ensure() is called from a new thread for the first time and PyEval_InitThreads() was not called yet, a GIL needs to be created. --- Python/pystate.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index 6be71de2ae..a56e308969 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -771,6 +771,11 @@ PyGILState_Ensure(void) assert(autoInterpreterState); /* Py_Initialize() hasn't been called! */ tcur = (PyThreadState *)PyThread_get_key_value(autoTLSkey); if (tcur == NULL) { + /* At startup, Python has no concrete GIL. If PyGILState_Ensure() is + called from a new thread for the first time, we need the create the + GIL. */ + PyEval_InitThreads(); + /* Create a new thread state for this thread */ tcur = PyThreadState_New(autoInterpreterState); if (tcur == NULL) -- cgit v1.2.1 From 63ac1fa663bbf793deb6f65ccea16f1bd8a5944c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 13 Dec 2013 11:08:56 +0100 Subject: Issue #19787: PyThread_set_key_value() now always set the value In Python 3.3, PyThread_set_key_value() did nothing if the key already exists (if the current value is a non-NULL pointer). When _PyGILState_NoteThreadState() is called twice on the same thread with a different Python thread state, it still keeps the old Python thread state to keep the old behaviour. Replacing the Python thread state with the new state introduces new bugs: see issues #10915 and #15751. --- Python/pystate.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'Python/pystate.c') diff --git a/Python/pystate.c b/Python/pystate.c index a56e308969..19fceb7127 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -723,18 +723,18 @@ _PyGILState_NoteThreadState(PyThreadState* tstate) The only situation where you can legitimately have more than one thread state for an OS level thread is when there are multiple - interpreters, when: + interpreters. - a) You shouldn't really be using the PyGILState_ APIs anyway, - and: + You shouldn't really be using the PyGILState_ APIs anyway (see issues + #10915 and #15751). - b) The slightly odd way PyThread_set_key_value works (see - comments by its implementation) means that the first thread - state created for that given OS level thread will "win", - which seems reasonable behaviour. + The first thread state created for that given OS level thread will + "win", which seems reasonable behaviour. */ - if (PyThread_set_key_value(autoTLSkey, (void *)tstate) < 0) - Py_FatalError("Couldn't create autoTLSkey mapping"); + if (PyThread_get_key_value(autoTLSkey) == NULL) { + if (PyThread_set_key_value(autoTLSkey, (void *)tstate) < 0) + Py_FatalError("Couldn't create autoTLSkey mapping"); + } /* PyGILState_Release must not try to delete this thread state. */ tstate->gilstate_counter = 1; -- cgit v1.2.1