Skip to content

Instantly share code, notes, and snippets.

@ericsnowcurrently
Last active November 17, 2022 00:30
Show Gist options
  • Save ericsnowcurrently/99e2c79281a02a407ddf1dd49bf2d321 to your computer and use it in GitHub Desktop.
Save ericsnowcurrently/99e2c79281a02a407ddf1dd49bf2d321 to your computer and use it in GitHub Desktop.
analysis of gilstate operations
_PyRuntimeState_GetThreadState()
_PyRuntime.gilstate.tstate_current <----
_PyRuntimeGILState_GetThreadState()
_PyRuntime.gilstate.tstate_current <----
_PyRuntimeGILState_SetThreadState(tstate)
_PyRuntime.gilstate.tstate_current = tstate <----
_PyThreadState_GET()
_PyRuntimeState_GetThreadState()
...
_PyInterpreterState_GET()
tstate = _PyThreadState_GET()
...
_Py_EnsureTstateNotNULL()
return tstate->interp
_PyGILState_GetInterpreterStateUnsafe()
_PyRuntime.gilstate.autoInterpreterState <----
_PyGILState_GetThisThreadState()
if _PyRuntime.gilstate.autoInterpreterState: <----
expected = PyThread_tss_get(_PyRuntime.gilstate.autoTSSkey) <----
else
expected = NULL
_PyGILState_NoteThreadState()
if _PyRuntime.gilstate.autoInterpreterState: <----
if PyThread_tss_get(_PyRuntime.gilstate.autoTSSkey) == NULL: <----
PyThread_tss_set(_PyRuntime.gilstate.autoTSSkey, newts) <----
_PyGILState_SetTstate()
if _Py_IsMainInterpreter(tstate.interp):
_PyRuntime.gilstate.autoInterpreterState = tstate.interp <----
_PyGILState_NoteThreadState(tstate)
...
PyThreadState_IsCurrent(tcur)
assert(_PyGILState_GetThisThreadState() == tcur)
...
current = (tcur == _PyRuntimeGILState_GetThreadState())
...
_PyThreadState_SetCurrent()
_PyGILState_NoteThreadState()
...
_PyThreadState_DeleteCurrent()
tstate_delete_common()
if _PyRuntime.gilstate.autoInterpreterState and <----
PyThread_tss_get(_PyRuntime.gilstate.autoTSSkey) == tstate: <----
PyThread_tss_set(_PyRuntime.gilstate.autoTSSkey, NULL) <----
_PyThreadState_Swap()
_PyRuntimeGILState_GetThreadState()
...
_PyRuntimeGILState_SetThreadState()
...
PyEval_SaveThread()
oldts = _PyThreadState_Swap(NULL)
...
_Py_EnsureTstateNotNULL(oldts)
assert(gil_created())
...
drop_gil()
if _PyRuntime.ceval.gil.locked:
_PyRuntime.ceval.gil.last_holder = tstate
_PyRuntime.ceval.gil.locked = 1
PyEval_RestoreThread()
_Py_EnsureTstateNotNULL()
take_gil()
...
_PyThreadState_Swap()
...
Py_BEGIN_ALLOW_THREADS
PyEval_SaveThread()
...
Py_END_ALLOW_THREADS
PyEval_RestoreThread()
...
PyThreadState_New(interp = _PyRuntime.gilstate.autoInterpreterState) <----
new_threadstate()
acquire-head-lock
| interp.threads.next_unique_id++
| newts = alloc_threadstate()
| interp.threads.head = tcur
| init_threadstate()
| tcur.interp = interp
- release-head-lock
_PyThreadState_SetCurrent()
...
PyGILState_Ensure()
assert(_PyEval_ThreadsInitialized())
gil_created()
actual = (_PyRuntime.ceval.gil.locked >= 0)
assert(_PyRuntime.gilstate.autoInterpreterState) <----
tcur = PyThread_tss_get(_PyRuntime.gilstate.autoTSSkey) <----
if tcur == NULL:
tcur = PyThreadState_New(interp = _PyRuntime.gilstate.autoInterpreterState) <----
...
current = False
else:
current = PyThreadState_IsCurrent(tcur)
...
return PyGILState_LOCKED if current else PyGILState_UNLOCKED
PyGILState_Release()
tstate = PyThread_tss_get(_PyRuntime.gilstate.autoTSSkey) <----
if PyThreadState_IsCurrent(tstate):
...
--tstate.gilstate_counter
if tstate.gilstate_counter == 0:
PyThreadState_Clear(tstate)
assert(_PyRuntimeGILState_GetThreadState() == tstate)
...
_PyThreadState_DeleteCurrent()
...
elif oldstate == PyGILState_UNLOCKED:
PyEval_SaveThread()
...
###############################################################################
# failing on some buildbots due to https://github.com/python/cpython/pull/99378
test.test_threading.ThreadTests.test_frame_tstate_tracing()
_testcapi.call_in_temporary_c_thread()
acquire-start-lock
acquire-exit-lock
PyThread_start_new_thread() -> temporary_c_thread()
_PyThreadState_GET()
...
pthread_create() -> pythread_wrapper() -> temporary_c_thread()
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
release-start-lock
PyGILState_Ensure()
...
PyObject_CallNoArgs()
Py_CLEAR()
PyGILState_Release()
...
release-exit-lock
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
block-on-start-lock
Py_BEGIN_ALLOW_THREADS
...
block-on-exit-lock
Py_END_ALLOW_THREADS
...
*******************************************************************************
main
lock "start"
lock "stop"
get _PyRuntime.gilstate.tstate_current (discarded)
block on "start"
new thread
release "start"
race
main
get _PyRuntime.gilstate.tstate_current -> oldts
set _PyRuntime.gilstate.tstate_current <- NULL
new thread
get _PyRuntime.gilstate.autoInterpreterState (discarded)
tss-get _PyRuntime.gilstate.autoTSSkey -> tcur
if tcur == NULL:
get _PyRuntime.gilstate.autoInterpreterState -> interp
get _PyRuntime.gilstate.autoInterpreterState (discarded)
tss-get _PyRuntime.gilstate.autoTSSkey (discarded)
tss-set _PyRuntime.gilstate.autoTSSkey <- tcur
else:
get _PyRuntime.gilstate.autoInterpreterState (discarded)
tss-get _PyRuntime.gilstate.autoTSSkey (discarded)
get _PyRuntime.gilstate.tstate_current (discarded)
<various uses via PyObject_CallNoArgs()>
tss-get _PyRuntime.gilstate.autoTSSkey -> tstate
get _PyRuntime.gilstate.tstate_current (discarded)
get _PyRuntime.gilstate.autoInterpreterState (discarded)
tss-get _PyRuntime.gilstate.autoTSSkey -> current
if current == tstate:
tss-set _PyRuntime.gilstate.autoTSSkey <- NULL
get _PyRuntime.gilstate.tstate_current -> ,,,
set _PyRuntime.gilstate.tstate_current <- NULL
main
block on "stop"
get _PyRuntime.gilstate.tstate_current (discarded)
set _PyRuntime.gilstate.tstate_current <- oldts
new thread
release "stop"
###############################################################################
# failing on some buildbots due to https://github.com/python/cpython/pull/99378
test.test_capi.test_misc.TestThreadState.test_gilstate_ensure_no_deadlock()
_testcapi._test_thread_state()
acquire-done-lock
PyThread_start_new_thread() -> pythread_wrapper() -> _make_call_from_thread()
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
_make_call()
PyGILState_Ensure()
...
PyObject_CallNoArgs()
PyGILState_Release()
...
release-done-lock
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_make_call()
...
Py_BEGIN_ALLOW_THREADS
...
_make_call()
...
block-on-done-lock
Py_END_ALLOW_THREADS
...
Py_BEGIN_ALLOW_THREADS
...
PyThread_start_new_thread()
_make_call()
...
block-on-done-lock
Py_END_ALLOW_THREADS
...
*******************************************************************************
...
###############################################################################
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment