Python Thread State¶
Get the current Python Thread State (tstate)¶
_PyRuntimeState_GetThreadState(runtime): readruntime->gilstate.tstate_current_PyThreadState_GET(): internal C API, call_PyRuntimeState_GetThreadState(&_PyRuntime), new in Python 3.8.PyThreadState_Get(): opaque function call_PyThreadState_UncheckedGet(): added to Python 3.5.2 (bpo-26154)PyGILState_GetThisThreadState()PyThreadState_GET(): macro, alias to PyThreadState_Get(). When pycore_pystate.h is included: macro redefined as an alias to _PyThreadState_GET().
There was also _PyThreadState_Current: removed from Python 3.5.1.
History:
Python 3.7:
PyThreadState_GET()reads_PyThreadState_Current(atomic variable).Python 3.8:
_PyThreadState_Currentbecomes_PyRuntime.gilstate.tstate_current
Get the current interpreter (interp)¶
PyInterpreterState_Get(): limited C API. new in Python 3.9. Known as_PyInterpreterState_Get()in Python 3.8. Implemented as_PyThreadState_GET()->interp._PyInterpreterState_GET(): internal C API, added to Python 3.8. Previously called_PyInterpreterState_GET_UNSAFE()in Python 3.8._PyGILState_GetInterpreterStateUnsafe(): read_PyRuntime.gilstate.autoInterpreterState, new in Python 3.6.
Python 3.9 also defines _PyInterpreterState_Get() as an alias to
PyInterpreterState_Get() for backward compatibility.
Main thread and main interpreter¶
PyInterpreterState_Main(): get_PyRuntime.interpreters.main_PyOS_IsMainThread(): call_Py_ThreadCanHandleSignals(_PyInterpreterState_GET())_Py_IsMainThread(): Check if the current thread is the main thread._Py_IsMainInterpreter(tstate)_Py_ThreadCanHandleSignals(interp): added by https://bugs.python.org/issue40010_Py_ThreadCanHandlePendingCalls(): see https://bugs.python.org/issue40231
Identifier of the main thread (of the main interpreter):
_PyRuntime.main_thread. Identifier read by
PyThread_get_thread_ident(), it’s not a Python thread state. Used
by _Py_IsMainThread().
Main interpreter: _PyRuntime.interpreters.main.
GIL¶
take_gil() and drop_gil() stores the Python thread state into
_PyRuntime.ceval.gil.last_holder.
Signal handler¶
trip_signal() of signalmodule.c¶
Python 3.9 now always uses the main interpreter (_PyRuntime.interpreters.main),
it no longer tries to get the current Python thread state:
bpo-40082.
_PyEval_SignalReceived(interp) sets signals_pending and
eval_breaker to 1.
Call _PyEval_AddPendingCall(interp, ...) if writing into the wakeup fd
fails.
Python 3.9 made eval_breaker and pending calls per interpreter.
On Windows, SIGINT (CTRL+C) signal handler is called from a different
thread at each call. MSDN signal documentation:
When a CTRL+C interrupt occurs, Win32 operating systems generate a new thread to specifically handle that interrupt.
This can cause a single-thread application, such as one in UNIX, to become multithreaded and cause unexpected behavior.
faulthandler¶
faulthandler_fatal_error() and faulthandler_user() signal handlers use
PyGILState_GetThisThreadState() to get the current Python thread state:
/* SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals and
are thus delivered to the thread that caused the fault. Get the Python
thread state of the current thread.
PyThreadState_Get() doesn't give the state of the thread that caused the
fault if the thread released the GIL, and so this function cannot be
used. Read the thread specific storage (TSS) instead: call
PyGILState_GetThisThreadState(). */
tstate = PyGILState_GetThisThreadState();
enable() and register() use _PyThreadState_UncheckedGet() and then
store PyThreadState_GetInterpreter(tstate) to use it from signal handlers.
tracemalloc¶
Hooks on memory allocators use PyGILState_GetThisThreadState() to get the
current Python thread state. PyMem_RawMalloc() can be called without
holding the GIL.
PyMem_RawMalloc() hook uses PyGILState_Ensure() and
PyGILState_Release() to hold the GIL.
PyGILState and subinterpreters¶
subinterpreters¶
It is currently possible for a single native thread to be associated to multiple Python thread states: one per interpreter.
Extract of _testcapi.run_in_subinterp() implementation:
PyThreadState *mainstate = PyThreadState_Get();
PyThreadState_Swap(NULL);
PyThreadState *substate = Py_NewInterpreter();
...
Py_EndInterpreter(substate);
PyThreadState_Swap(mainstate);
Py_NewInterpreter() creates a new Python thread state. Extract of its
implementation:
PyInterpreterState *interp = PyInterpreterState_New();
...
PyThreadState *tstate = PyThreadState_New(interp);
..
PyThreadState_Swap(tstate);
Pass tstate explicitly¶
Pass the Python thread state explicitly (January 2020) by Victor Stinner.
Move global variables into PyInterpreterState¶
PyLong_FromLong() now requires to get the current interpreter to access
PyInterpreterState.small_ints singletons.
Thread-local storage¶
Mark Shannon: experiment to moving the Python thread state to thread-local storage (TLS):