From 87f514b8a4fc6b29df8febf1176ad846007a5b56 Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Thu, 8 Jun 2023 18:34:20 +0200 Subject: [PATCH] Fix for what may be a CPython 3.12 bug, or just me abusing the API --- c/misc_thread_common.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h index 66e28354..9e9a818d 100644 --- a/c/misc_thread_common.h +++ b/c/misc_thread_common.h @@ -102,6 +102,7 @@ thread_canary_dealloc(ThreadCanaryObj *ob) 'local_thread_canary' accesses need to be protected with the TLS_ZOM_LOCK. */ + //fprintf(stderr, "entering dealloc(%p)\n", ob); TLS_ZOM_LOCK(); if (ob->zombie_next != NULL) { //fprintf(stderr, "thread_canary_dealloc(%p): ZOMBIE\n", ob); @@ -136,6 +137,7 @@ thread_canary_make_zombie(ThreadCanaryObj *ob) ob->zombie_prev = last; last->zombie_next = ob; cffi_zombie_head.zombie_prev = ob; + //fprintf(stderr, "thread_canary_make_zombie(%p) DONE\n", ob); } static void @@ -164,6 +166,15 @@ thread_canary_free_zombies(void) break; PyThreadState_Clear(tstate); /* calls thread_canary_dealloc on 'ob', but now ob->zombie_next == NULL. */ +#if PY_HEX_VERSION >= 0x030C0000 + /* this might be a bug introduced in 3.12, or just me abusing the + API around there. The issue is that PyThreadState_Delete() + called on a random old tstate will clear the *current* thread + notion of what PyGILState_GetThisThreadState() should be, at + least if the internal 'bound_gilstate' flag is set in the old + tstate. Workaround: clear that flag. */ + tstate->_status.bound_gilstate = 0; +#endif PyThreadState_Delete(tstate); //fprintf(stderr, "thread_canary_free_zombie: cleared and deleted tstate=%p\n", tstate); } @@ -213,9 +224,11 @@ thread_canary_register(PyThreadState *tstate) tstate->gilstate_counter++; /* ^^^ this means 'tstate' will never be automatically freed by PyGILState_Release() */ + //fprintf(stderr, "CANARY: ready, tstate=%p, tls=%p, canary=%p\n", tstate, tls, canary); return; ignore_error: + //fprintf(stderr, "CANARY: IGNORED ERROR\n"); PyErr_Clear(); } @@ -334,6 +347,7 @@ static PyGILState_STATE gil_ensure(void) */ PyGILState_STATE result; PyThreadState *ts = PyGILState_GetThisThreadState(); + //fprintf(stderr, "%p: gil_ensure(), tstate=%p, tls=%p\n", get_cffi_tls(), ts, get_cffi_tls()); if (ts != NULL) { ts->gilstate_counter++; @@ -341,9 +355,11 @@ static PyGILState_STATE gil_ensure(void) /* common case: 'ts' is our non-current thread state and we have to make it current and acquire the GIL */ PyEval_RestoreThread(ts); + //fprintf(stderr, "%p: gil_ensure(), tstate=%p MADE CURRENT\n", get_cffi_tls(), ts); return PyGILState_UNLOCKED; } else { + //fprintf(stderr, "%p: gil_ensure(), tstate=%p ALREADY CURRENT\n", get_cffi_tls(), ts); return PyGILState_LOCKED; } } @@ -353,6 +369,7 @@ static PyGILState_STATE gil_ensure(void) assert(result == PyGILState_UNLOCKED); ts = PyGILState_GetThisThreadState(); + //fprintf(stderr, "%p: gil_ensure(), made a new tstate=%p\n", get_cffi_tls(), ts); assert(ts != NULL); assert(ts == get_current_ts()); assert(ts->gilstate_counter >= 1); @@ -361,11 +378,13 @@ static PyGILState_STATE gil_ensure(void) thread really shuts down */ thread_canary_register(ts); + assert(ts == PyGILState_GetThisThreadState()); return result; } } static void gil_release(PyGILState_STATE oldstate) { + //fprintf(stderr, "%p: gil_release(%d), tls=%p\n", get_cffi_tls(), (int)oldstate, get_cffi_tls()); PyGILState_Release(oldstate); } -- GitLab