summaryrefslogtreecommitdiff
path: root/Include/pystate.h
diff options
context:
space:
mode:
Diffstat (limited to 'Include/pystate.h')
-rw-r--r--Include/pystate.h46
1 files changed, 36 insertions, 10 deletions
diff --git a/Include/pystate.h b/Include/pystate.h
index 2017b0276f..06af808444 100644
--- a/Include/pystate.h
+++ b/Include/pystate.h
@@ -69,6 +69,7 @@ typedef struct _ts PyThreadState;
typedef struct _ts {
/* See Python/ceval.c for comments explaining most fields */
+ struct _ts *prev;
struct _ts *next;
PyInterpreterState *interp;
@@ -99,16 +100,6 @@ typedef struct _ts {
PyObject *dict; /* Stores per-thread state */
- /* XXX doesn't mean anything anymore (the comment below is obsolete)
- => deprecate or remove? */
- /* tick_counter is incremented whenever the check_interval ticker
- * reaches zero. The purpose is to give a useful measure of the number
- * of interpreted bytecode instructions in a given thread. This
- * extremely lightweight statistic collector may be of interest to
- * profilers (like psyco.jit()), although nothing in the core uses it.
- */
- int tick_counter;
-
int gilstate_counter;
PyObject *async_exc; /* Asynchronous exception to raise */
@@ -117,6 +108,32 @@ typedef struct _ts {
int trash_delete_nesting;
PyObject *trash_delete_later;
+ /* Called when a thread state is deleted normally, but not when it
+ * is destroyed after fork().
+ * Pain: to prevent rare but fatal shutdown errors (issue 18808),
+ * Thread.join() must wait for the join'ed thread's tstate to be unlinked
+ * from the tstate chain. That happens at the end of a thread's life,
+ * in pystate.c.
+ * The obvious way doesn't quite work: create a lock which the tstate
+ * unlinking code releases, and have Thread.join() wait to acquire that
+ * lock. The problem is that we _are_ at the end of the thread's life:
+ * if the thread holds the last reference to the lock, decref'ing the
+ * lock will delete the lock, and that may trigger arbitrary Python code
+ * if there's a weakref, with a callback, to the lock. But by this time
+ * _PyThreadState_Current is already NULL, so only the simplest of C code
+ * can be allowed to run (in particular it must not be possible to
+ * release the GIL).
+ * So instead of holding the lock directly, the tstate holds a weakref to
+ * the lock: that's the value of on_delete_data below. Decref'ing a
+ * weakref is harmless.
+ * on_delete points to _threadmodule.c's static release_sentinel() function.
+ * After the tstate is unlinked, release_sentinel is called with the
+ * weakref-to-lock (on_delete_data) argument, and release_sentinel releases
+ * the indirectly held lock.
+ */
+ void (*on_delete)(void *);
+ void *on_delete_data;
+
/* XXX signal handlers should also be here */
} PyThreadState;
@@ -133,12 +150,16 @@ PyAPI_FUNC(int) PyState_AddModule(PyObject*, struct PyModuleDef*);
PyAPI_FUNC(int) PyState_RemoveModule(struct PyModuleDef*);
#endif
PyAPI_FUNC(PyObject*) PyState_FindModule(struct PyModuleDef*);
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void) _PyState_ClearModules(void);
+#endif
PyAPI_FUNC(PyThreadState *) PyThreadState_New(PyInterpreterState *);
PyAPI_FUNC(PyThreadState *) _PyThreadState_Prealloc(PyInterpreterState *);
PyAPI_FUNC(void) _PyThreadState_Init(PyThreadState *);
PyAPI_FUNC(void) PyThreadState_Clear(PyThreadState *);
PyAPI_FUNC(void) PyThreadState_Delete(PyThreadState *);
+PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate);
#ifdef WITH_THREAD
PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
PyAPI_FUNC(void) _PyGILState_Reinit(void);
@@ -212,6 +233,11 @@ PyAPI_FUNC(void) PyGILState_Release(PyGILState_STATE);
*/
PyAPI_FUNC(PyThreadState *) PyGILState_GetThisThreadState(void);
+/* Helper/diagnostic function - return 1 if the current thread
+ * currently holds the GIL, 0 otherwise
+ */
+PyAPI_FUNC(int) PyGILState_Check(void);
+
#endif /* #ifdef WITH_THREAD */
/* The implementation of sys._current_frames() Returns a dict mapping