From 414cfef83fdec3773595d25049a2834d6c428895 Mon Sep 17 00:00:00 2001 From: ruang Date: Wed, 16 Oct 2024 10:46:38 +0800 Subject: [PATCH 01/24] Add threads.mutex member to PyInterpreState --- Include/internal/pycore_interp.h | 2 ++ Include/internal/pycore_pystate.h | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index a1c1dd0c957230..a73432a11aa87d 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -139,6 +139,8 @@ struct _is { or the size specified by the THREAD_STACK_SIZE macro. */ /* Used in Python/thread.c. */ size_t stacksize; + /* The mutex lock of the current executing thread. */ + PyMutex mutex; } threads; /* Reference to the _PyRuntime global variable. This field exists diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index fade55945b7dbf..6f400d982ef696 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -263,6 +263,11 @@ extern int _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_UNLOCK(runtime) \ PyMutex_Unlock(&(runtime)->interpreters.mutex) +#define INTERP_THREAD_LOCK(interpstate) \ + PyMutex_LockFlags(&(interpstate)->threads.mutex, _Py_LOCK_DONT_DETACH) +#define INTERP_THREAD_UNLOCK(interpstate) \ + PyMutex_Unlock(&(interpstate)->threads.mutex) + // Get the configuration of the current interpreter. // The caller must hold the GIL. // Export for test_peg_generator. From 0c815fcffbac16a79c62224408ba41de51cd57ed Mon Sep 17 00:00:00 2001 From: ruang Date: Wed, 16 Oct 2024 11:11:18 +0800 Subject: [PATCH 02/24] Add NEWS --- .../2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst new file mode 100644 index 00000000000000..be888f2a37ff82 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst @@ -0,0 +1,2 @@ +Added the :c:member:`PyInterpreterState.threads.mutex` to +:c:type:`PyInterpreterState`. Patch by RUANG. From f5ec949fe1508bd5c55d4861c36aa4b2b3c2c554 Mon Sep 17 00:00:00 2001 From: "RUANG (Roy James)" Date: Wed, 16 Oct 2024 19:32:43 +0800 Subject: [PATCH 03/24] Delete Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst --- .../2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst deleted file mode 100644 index be888f2a37ff82..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-16-11-07-50.gh-issue-114940.Sev-r-.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added the :c:member:`PyInterpreterState.threads.mutex` to -:c:type:`PyInterpreterState`. Patch by RUANG. From b595153587c5dae374e941df0fbaae197de8a85c Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 17 Oct 2024 10:54:51 +0800 Subject: [PATCH 04/24] Change to current thread mutex --- Python/ceval.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 2a5c16aa101985..d74e1c344cf15a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2341,20 +2341,20 @@ void PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg) { PyThreadState *this_tstate = _PyThreadState_GET(); - PyInterpreterState* interp = this_tstate->interp; + PyInterpreterState *interp = this_tstate->interp; _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); + INTERP_THREAD_LOCK(interp); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); while (ts) { if (_PyEval_SetProfile(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfileAllThreads"); } - HEAD_LOCK(runtime); + INTERP_THREAD_LOCK(interp); ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); } } @@ -2372,20 +2372,20 @@ void PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg) { PyThreadState *this_tstate = _PyThreadState_GET(); - PyInterpreterState* interp = this_tstate->interp; + PyInterpreterState *interp = this_tstate->interp; _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); + INTERP_THREAD_LOCK(interp); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); while (ts) { if (_PyEval_SetTrace(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetTraceAllThreads"); } - HEAD_LOCK(runtime); + INTERP_THREAD_LOCK(interp); ts = PyThreadState_Next(ts); - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); } } From 2378caa766fe43412ba3c8c82d8c86569bcc4ade Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 17 Oct 2024 11:25:55 +0800 Subject: [PATCH 05/24] Remove runtime variable --- Python/ceval.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index d74e1c344cf15a..c572d05fbacb12 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2343,7 +2343,6 @@ PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - _PyRuntimeState *runtime = &_PyRuntime; INTERP_THREAD_LOCK(interp); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); INTERP_THREAD_UNLOCK(interp); @@ -2374,7 +2373,6 @@ PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - _PyRuntimeState *runtime = &_PyRuntime; INTERP_THREAD_LOCK(interp); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); INTERP_THREAD_UNLOCK(interp); From 9cb2de0dc32de3f9a45c3fd3de5475023f4d28fa Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 17 Oct 2024 19:26:57 +0800 Subject: [PATCH 06/24] HEAD_LOCK change to INTERP_THREAD_LOCK --- Python/gc_free_threading.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 54de0c2671ae68..ba68863cc984be 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -323,9 +323,9 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor, assert(interp->stoptheworld.world_stopped); int err; - HEAD_LOCK(&_PyRuntime); + INTERP_THREAD_LOCK(interp); err = gc_visit_heaps_lock_held(interp, visitor, arg); - HEAD_UNLOCK(&_PyRuntime); + INTERP_THREAD_UNLOCK(interp); return err; } @@ -347,7 +347,7 @@ gc_visit_stackref(_PyStackRef stackref) static void gc_visit_thread_stacks(PyInterpreterState *interp) { - HEAD_LOCK(&_PyRuntime); + INTERP_THREAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { _PyInterpreterFrame *f = p->current_frame; while (f != NULL) { @@ -361,7 +361,7 @@ gc_visit_thread_stacks(PyInterpreterState *interp) f = f->previous; } } - HEAD_UNLOCK(&_PyRuntime); + INTERP_THREAD_UNLOCK(interp); } static void @@ -397,13 +397,13 @@ process_delayed_frees(PyInterpreterState *interp) _Py_qsbr_advance(&interp->qsbr); _PyThreadStateImpl *current_tstate = (_PyThreadStateImpl *)_PyThreadState_GET(); _Py_qsbr_quiescent_state(current_tstate->qsbr); - HEAD_LOCK(&_PyRuntime); + INTERP_THREAD_LOCK(interp); PyThreadState *tstate = interp->threads.head; while (tstate != NULL) { _PyMem_ProcessDelayed(tstate); tstate = (PyThreadState *)tstate->next; } - HEAD_UNLOCK(&_PyRuntime); + INTERP_THREAD_UNLOCK(interp); } // Subtract an incoming reference from the computed "gc_refs" refcount. @@ -1185,7 +1185,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, state->gcstate->old[i-1].count = 0; } - HEAD_LOCK(&_PyRuntime); + INTERP_THREAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p; @@ -1195,7 +1195,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, // merge refcounts for all queued objects merge_queued_objects(tstate, state); } - HEAD_UNLOCK(&_PyRuntime); + INTERP_THREAD_UNLOCK(interp); process_delayed_frees(interp); @@ -1953,13 +1953,13 @@ PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) void _PyGC_ClearAllFreeLists(PyInterpreterState *interp) { - HEAD_LOCK(&_PyRuntime); + INTERP_THREAD_LOCK(interp); _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)interp->threads.head; while (tstate != NULL) { _PyObject_ClearFreeLists(&tstate->freelists, 0); tstate = (_PyThreadStateImpl *)tstate->base.next; } - HEAD_UNLOCK(&_PyRuntime); + INTERP_THREAD_UNLOCK(interp); } #endif // Py_GIL_DISABLED From 29c4ccb9e4e041132ba9bda298130cd65bd0147a Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 17 Oct 2024 20:38:24 +0800 Subject: [PATCH 07/24] HEAD_LOCK change to INTERP_THREAD_LOCK --- Python/instrumentation.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Python/instrumentation.c b/Python/instrumentation.c index e1e494c31a1120..6cc3e0c15c0258 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -988,13 +988,12 @@ set_global_version(PyThreadState *tstate, uint32_t version) #ifdef Py_GIL_DISABLED // Set the version on all threads in free-threaded builds. - _PyRuntimeState *runtime = &_PyRuntime; - HEAD_LOCK(runtime); + INTERP_THREAD_LOCK(interp); for (tstate = interp->threads.head; tstate; tstate = PyThreadState_Next(tstate)) { set_version_raw(&tstate->eval_breaker, version); }; - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); #else // Normal builds take the current version from instrumentation_version when // attaching a thread, so we only have to set the current thread's version. From 462f97ddaef0d169d611f30edd666c5be4587535 Mon Sep 17 00:00:00 2001 From: ruang Date: Fri, 18 Oct 2024 13:52:37 +0800 Subject: [PATCH 08/24] HEAD_LOCK change to INTERP_THREAD_LOCK --- Python/pystate.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index e3812cba41d9c2..57bf9aa7cac5f5 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -791,17 +791,17 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) } // Clear the current/main thread state last. - HEAD_LOCK(runtime); - PyThreadState *p = interp->threads.head; - HEAD_UNLOCK(runtime); + INTERP_THREAD_LOCK(interp); + PyThreadState *p = PyInterpreterState_ThreadHead(interp); + INTERP_THREAD_UNLOCK(interp); while (p != NULL) { // See https://github.com/python/cpython/issues/102126 // Must be called without HEAD_LOCK held as it can deadlock // if any finalizer tries to acquire that lock. PyThreadState_Clear(p); - HEAD_LOCK(runtime); + INTERP_THREAD_LOCK(interp); p = p->next; - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); } if (tstate->interp == interp) { /* We fix tstate->_status below when we for sure aren't using it @@ -1854,10 +1854,10 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) assert(runtime->stoptheworld.world_stopped); #endif - HEAD_LOCK(runtime); + INTERP_THREAD_LOCK(interp); /* Remove all thread states, except tstate, from the linked list of thread states. */ - PyThreadState *list = interp->threads.head; + PyThreadState *list = PyInterpreterState_ThreadHead(interp); if (list == tstate) { list = tstate->next; } @@ -1869,7 +1869,7 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) } tstate->prev = tstate->next = NULL; interp->threads.head = tstate; - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); return list; } @@ -2345,8 +2345,9 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) * list of thread states we're traversing, so to prevent that we lock * head_mutex for the duration. */ - HEAD_LOCK(runtime); - for (PyThreadState *tstate = interp->threads.head; tstate != NULL; tstate = tstate->next) { + INTERP_THREAD_LOCK(interp); + PyThreadState *list = PyInterpreterState_ThreadHead(interp); + for (PyThreadState *tstate = list; tstate != NULL; tstate = tstate->next) { if (tstate->thread_id != id) { continue; } @@ -2360,13 +2361,13 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) */ Py_XINCREF(exc); PyObject *old_exc = _Py_atomic_exchange_ptr(&tstate->async_exc, exc); - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); Py_XDECREF(old_exc); _Py_set_eval_breaker_bit(tstate, _PY_ASYNC_EXCEPTION_BIT); return 1; } - HEAD_UNLOCK(runtime); + INTERP_THREAD_UNLOCK(interp); return 0; } From 8146b6f5c8a59494fde98914f31967b2f7e4a46d Mon Sep 17 00:00:00 2001 From: ruang Date: Fri, 18 Oct 2024 13:59:20 +0800 Subject: [PATCH 09/24] Remove unused variable --- Python/pystate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index 57bf9aa7cac5f5..d4d050a11292c2 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -774,7 +774,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) { assert(interp != NULL); assert(tstate != NULL); - _PyRuntimeState *runtime = interp->runtime; /* XXX Conditions we need to enforce: @@ -1852,6 +1851,8 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) #ifdef Py_GIL_DISABLED assert(runtime->stoptheworld.world_stopped); +#else + assert(runtime != NULL); #endif INTERP_THREAD_LOCK(interp); @@ -2336,7 +2337,6 @@ _PyEval_StartTheWorld(PyInterpreterState *interp) int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) { - _PyRuntimeState *runtime = &_PyRuntime; PyInterpreterState *interp = _PyInterpreterState_GET(); /* Although the GIL is held, a few C API functions can be called From 74cc0605617356c89c5b7de4c94440c66cc788fc Mon Sep 17 00:00:00 2001 From: ruang Date: Fri, 18 Oct 2024 14:35:18 +0800 Subject: [PATCH 10/24] Clear unused code --- Python/pystate.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index d4d050a11292c2..fb6f9a6f9aad58 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1847,13 +1847,6 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) { assert(tstate != NULL); PyInterpreterState *interp = tstate->interp; - _PyRuntimeState *runtime = interp->runtime; - -#ifdef Py_GIL_DISABLED - assert(runtime->stoptheworld.world_stopped); -#else - assert(runtime != NULL); -#endif INTERP_THREAD_LOCK(interp); /* Remove all thread states, except tstate, from the linked list of From f30b2b132e4522bd02e2b82cdac02340c46eb86f Mon Sep 17 00:00:00 2001 From: ruang Date: Sat, 19 Oct 2024 09:08:37 +0800 Subject: [PATCH 11/24] Change comment --- Include/internal/pycore_interp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 904a9ee7ebe088..8cba26ec3b1334 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -138,7 +138,7 @@ struct _is { or the size specified by the THREAD_STACK_SIZE macro. */ /* Used in Python/thread.c. */ size_t stacksize; - /* The mutex lock of the current executing thread. */ + /* The lock that protects this struct. */ PyMutex mutex; } threads; From 8e0d324777853ba858d14de3e20c3dbc7a320960 Mon Sep 17 00:00:00 2001 From: ruang Date: Sat, 19 Oct 2024 09:08:50 +0800 Subject: [PATCH 12/24] INTERP_THREAD_LOCK rename to INTERP_HEAD_LOCK --- Include/internal/pycore_pystate.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 6f400d982ef696..b4167d6cf9c70a 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -263,9 +263,9 @@ extern int _PyOS_InterruptOccurred(PyThreadState *tstate); #define HEAD_UNLOCK(runtime) \ PyMutex_Unlock(&(runtime)->interpreters.mutex) -#define INTERP_THREAD_LOCK(interpstate) \ +#define INTERP_HEAD_LOCK(interpstate) \ PyMutex_LockFlags(&(interpstate)->threads.mutex, _Py_LOCK_DONT_DETACH) -#define INTERP_THREAD_UNLOCK(interpstate) \ +#define INTERP_HEAD_UNLOCK(interpstate) \ PyMutex_Unlock(&(interpstate)->threads.mutex) // Get the configuration of the current interpreter. From 1f488c7612f640455dc19a253fae533b0639c7b9 Mon Sep 17 00:00:00 2001 From: ruang Date: Sat, 19 Oct 2024 09:09:25 +0800 Subject: [PATCH 13/24] INTERP_THREAD_LOCK rename to INTERP_HEAD_LOCK --- Python/gc_free_threading.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 2310a09fbda26e..1324e799eed9a3 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -344,9 +344,9 @@ gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor, assert(interp->stoptheworld.world_stopped); int err; - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); err = gc_visit_heaps_lock_held(interp, visitor, arg); - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); return err; } @@ -368,7 +368,7 @@ gc_visit_stackref(_PyStackRef stackref) static void gc_visit_thread_stacks(PyInterpreterState *interp) { - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { for (_PyInterpreterFrame *f = p->current_frame; f != NULL; f = f->previous) { PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); @@ -384,7 +384,7 @@ gc_visit_thread_stacks(PyInterpreterState *interp) } } } - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); } static void @@ -423,14 +423,14 @@ process_delayed_frees(PyInterpreterState *interp) // Merge the queues from other threads into our own queue so that we can // process all of the pending delayed free requests at once. - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { _PyThreadStateImpl *other = (_PyThreadStateImpl *)p; if (other != current_tstate) { llist_concat(¤t_tstate->mem_free_queue, &other->mem_free_queue); } } - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); _PyMem_ProcessDelayed((PyThreadState *)current_tstate); } @@ -1217,7 +1217,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, state->gcstate->old[i-1].count = 0; } - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p; @@ -1227,7 +1227,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state, // merge refcounts for all queued objects merge_queued_objects(tstate, state); } - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); process_delayed_frees(interp); @@ -1985,13 +1985,13 @@ PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) void _PyGC_ClearAllFreeLists(PyInterpreterState *interp) { - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)interp->threads.head; while (tstate != NULL) { _PyObject_ClearFreeLists(&tstate->freelists, 0); tstate = (_PyThreadStateImpl *)tstate->base.next; } - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); } #endif // Py_GIL_DISABLED From c99e44ed6bcc4b5408cca7985a530fa312e756d4 Mon Sep 17 00:00:00 2001 From: ruang Date: Sat, 19 Oct 2024 09:09:37 +0800 Subject: [PATCH 14/24] INTERP_THREAD_LOCK rename to INTERP_HEAD_LOCK --- Python/instrumentation.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 6cc3e0c15c0258..43ea67cc274b46 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -988,12 +988,12 @@ set_global_version(PyThreadState *tstate, uint32_t version) #ifdef Py_GIL_DISABLED // Set the version on all threads in free-threaded builds. - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); for (tstate = interp->threads.head; tstate; tstate = PyThreadState_Next(tstate)) { set_version_raw(&tstate->eval_breaker, version); }; - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); #else // Normal builds take the current version from instrumentation_version when // attaching a thread, so we only have to set the current thread's version. From 7ad9c323581ffcd3591c9c49ecf7f0bbe3c32b35 Mon Sep 17 00:00:00 2001 From: ruang Date: Sat, 19 Oct 2024 09:09:53 +0800 Subject: [PATCH 15/24] Using raw member --- Python/pystate.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index fb6f9a6f9aad58..2e58efffdc66e4 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -790,17 +790,17 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) } // Clear the current/main thread state last. - INTERP_THREAD_LOCK(interp); - PyThreadState *p = PyInterpreterState_ThreadHead(interp); - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_LOCK(interp); + PyThreadState *p = interp->threads.head; + INTERP_HEAD_UNLOCK(interp); while (p != NULL) { // See https://github.com/python/cpython/issues/102126 // Must be called without HEAD_LOCK held as it can deadlock // if any finalizer tries to acquire that lock. PyThreadState_Clear(p); - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); p = p->next; - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); } if (tstate->interp == interp) { /* We fix tstate->_status below when we for sure aren't using it @@ -1848,10 +1848,10 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) assert(tstate != NULL); PyInterpreterState *interp = tstate->interp; - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); /* Remove all thread states, except tstate, from the linked list of thread states. */ - PyThreadState *list = PyInterpreterState_ThreadHead(interp); + PyThreadState *list = interp->threads.head; if (list == tstate) { list = tstate->next; } @@ -1863,7 +1863,7 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) } tstate->prev = tstate->next = NULL; interp->threads.head = tstate; - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); return list; } @@ -2338,9 +2338,8 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) * list of thread states we're traversing, so to prevent that we lock * head_mutex for the duration. */ - INTERP_THREAD_LOCK(interp); - PyThreadState *list = PyInterpreterState_ThreadHead(interp); - for (PyThreadState *tstate = list; tstate != NULL; tstate = tstate->next) { + INTERP_HEAD_LOCK(interp); + for (PyThreadState *tstate = interp->threads.head; tstate != NULL; tstate = tstate->next) { if (tstate->thread_id != id) { continue; } @@ -2354,13 +2353,13 @@ PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc) */ Py_XINCREF(exc); PyObject *old_exc = _Py_atomic_exchange_ptr(&tstate->async_exc, exc); - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); Py_XDECREF(old_exc); _Py_set_eval_breaker_bit(tstate, _PY_ASYNC_EXCEPTION_BIT); return 1; } - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); return 0; } From 2131d69b0fe716cc5d9a67f7d04128219b945f54 Mon Sep 17 00:00:00 2001 From: ruang Date: Sat, 19 Oct 2024 10:43:41 +0800 Subject: [PATCH 16/24] Fix possible race conditions --- Python/pystate.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Python/pystate.c b/Python/pystate.c index 2e58efffdc66e4..353ddad794c130 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -792,16 +792,14 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) // Clear the current/main thread state last. INTERP_HEAD_LOCK(interp); PyThreadState *p = interp->threads.head; - INTERP_HEAD_UNLOCK(interp); while (p != NULL) { // See https://github.com/python/cpython/issues/102126 // Must be called without HEAD_LOCK held as it can deadlock // if any finalizer tries to acquire that lock. PyThreadState_Clear(p); - INTERP_HEAD_LOCK(interp); p = p->next; - INTERP_HEAD_UNLOCK(interp); } + INTERP_HEAD_UNLOCK(interp); if (tstate->interp == interp) { /* We fix tstate->_status below when we for sure aren't using it (e.g. no longer need the GIL). */ From 7bf9a1cd4e9b0fd2d5e6a7d3f024c3deb2ae5ee7 Mon Sep 17 00:00:00 2001 From: ruang Date: Sat, 19 Oct 2024 10:49:57 +0800 Subject: [PATCH 17/24] INTERP_THREAD_LOCK rename to INTERP_HEAD_LOCK --- Python/ceval.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index ef43c1077625d2..f2622769b6a9a0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2378,17 +2378,17 @@ PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); while (ts) { if (_PyEval_SetProfile(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfileAllThreads"); } - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); ts = PyThreadState_Next(ts); - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); } } @@ -2408,17 +2408,17 @@ PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); while (ts) { if (_PyEval_SetTrace(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetTraceAllThreads"); } - INTERP_THREAD_LOCK(interp); + INTERP_HEAD_LOCK(interp); ts = PyThreadState_Next(ts); - INTERP_THREAD_UNLOCK(interp); + INTERP_HEAD_UNLOCK(interp); } } From b947bc9768ae36db01a3d669817b4ef1d68d247d Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 20 Oct 2024 19:28:06 +0800 Subject: [PATCH 18/24] Rollback to unmodified --- Python/ceval.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index f2622769b6a9a0..0c9da3979c7d90 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2378,17 +2378,16 @@ PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - INTERP_HEAD_LOCK(interp); - PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - INTERP_HEAD_UNLOCK(interp); - + HEAD_LOCK(&_PyRuntime); + PyThreadState *ts = PyInterpreterState_ThreadHead(interp); + HEAD_UNLOCK(&_PyRuntime); while (ts) { if (_PyEval_SetProfile(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfileAllThreads"); } - INTERP_HEAD_LOCK(interp); + HEAD_LOCK(&_PyRuntime); ts = PyThreadState_Next(ts); - INTERP_HEAD_UNLOCK(interp); + HEAD_UNLOCK(&_PyRuntime); } } @@ -2408,17 +2407,16 @@ PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - INTERP_HEAD_LOCK(interp); - PyThreadState* ts = PyInterpreterState_ThreadHead(interp); - INTERP_HEAD_UNLOCK(interp); - + HEAD_LOCK(&_PyRuntime); + PyThreadState *ts = PyInterpreterState_ThreadHead(interp); + HEAD_UNLOCK(&_PyRuntime); while (ts) { if (_PyEval_SetTrace(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetTraceAllThreads"); } - INTERP_HEAD_LOCK(interp); + HEAD_LOCK(&_PyRuntime); ts = PyThreadState_Next(ts); - INTERP_HEAD_UNLOCK(interp); + HEAD_UNLOCK(&_PyRuntime); } } From 46c27098a23012c398781b20ed1c9586dc84caa0 Mon Sep 17 00:00:00 2001 From: ruang Date: Sun, 20 Oct 2024 19:32:48 +0800 Subject: [PATCH 19/24] Use variable --- Python/ceval.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 0c9da3979c7d90..4ec3c7b19a46f6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2378,16 +2378,18 @@ PyEval_SetProfileAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - HEAD_LOCK(&_PyRuntime); + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); PyThreadState *ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(&_PyRuntime); + HEAD_UNLOCK(runtime); + while (ts) { if (_PyEval_SetProfile(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetProfileAllThreads"); } - HEAD_LOCK(&_PyRuntime); + HEAD_LOCK(runtime); ts = PyThreadState_Next(ts); - HEAD_UNLOCK(&_PyRuntime); + HEAD_UNLOCK(runtime); } } @@ -2407,16 +2409,18 @@ PyEval_SetTraceAllThreads(Py_tracefunc func, PyObject *arg) PyThreadState *this_tstate = _PyThreadState_GET(); PyInterpreterState *interp = this_tstate->interp; - HEAD_LOCK(&_PyRuntime); + _PyRuntimeState *runtime = &_PyRuntime; + HEAD_LOCK(runtime); PyThreadState *ts = PyInterpreterState_ThreadHead(interp); - HEAD_UNLOCK(&_PyRuntime); + HEAD_UNLOCK(runtime); + while (ts) { if (_PyEval_SetTrace(ts, func, arg) < 0) { PyErr_FormatUnraisable("Exception ignored in PyEval_SetTraceAllThreads"); } - HEAD_LOCK(&_PyRuntime); + HEAD_LOCK(runtime); ts = PyThreadState_Next(ts); - HEAD_UNLOCK(&_PyRuntime); + HEAD_UNLOCK(runtime); } } From 3c208c2505cf22c620b3b9e5b1d3e7b33b2bb248 Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 24 Oct 2024 08:35:28 +0800 Subject: [PATCH 20/24] Recover assert --- Python/pystate.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Python/pystate.c b/Python/pystate.c index 353ddad794c130..6b4e1d470f8dc7 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1845,6 +1845,11 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) { assert(tstate != NULL); PyInterpreterState *interp = tstate->interp; + _PyRuntimeState *runtime = interp->runtime; + +#ifdef Py_GIL_DISABLED + assert(runtime->stoptheworld.world_stopped); +#endif INTERP_HEAD_LOCK(interp); /* Remove all thread states, except tstate, from the linked list of From 2bb60d9a02d35abcd889d16c46fde7522314a0bc Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 24 Oct 2024 09:17:41 +0800 Subject: [PATCH 21/24] Add mutex for get_mimalloc_allocated_blocks --- Objects/obmalloc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index dfeccfa4dd7658..2c7b8ceaa23660 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -1405,6 +1405,7 @@ get_mimalloc_allocated_blocks(PyInterpreterState *interp) { size_t allocated_blocks = 0; #ifdef Py_GIL_DISABLED + INTERP_HEAD_LOCK(interp); for (PyThreadState *t = interp->threads.head; t != NULL; t = t->next) { _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)t; for (int i = 0; i < _Py_MIMALLOC_HEAP_COUNT; i++) { @@ -1418,6 +1419,7 @@ get_mimalloc_allocated_blocks(PyInterpreterState *interp) _mi_abandoned_pool_visit_blocks(pool, tag, false, &count_blocks, &allocated_blocks); } + INTERP_HEAD_UNLOCK(interp); #else // TODO(sgross): this only counts the current thread's blocks. mi_heap_t *heap = mi_heap_get_default(); From 3798bed297d1c56c13fb69a847891924f1802d5a Mon Sep 17 00:00:00 2001 From: ruang Date: Thu, 24 Oct 2024 09:30:39 +0800 Subject: [PATCH 22/24] Add mutex for get_reftotal --- Objects/object.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/object.c b/Objects/object.c index 4a4c5bf7d7f08a..fdb7319013c33f 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -118,11 +118,13 @@ get_reftotal(PyInterpreterState *interp) since we can't determine which interpreter updated it. */ Py_ssize_t total = REFTOTAL(interp); #ifdef Py_GIL_DISABLED + INTERP_HEAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { /* This may race with other threads modifications to their reftotal */ _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)p; total += _Py_atomic_load_ssize_relaxed(&tstate_impl->reftotal); } + INTERP_HEAD_UNLOCK(interp); #endif return total; } From 6257837180fe2485893f559cccfcbb899feebcb6 Mon Sep 17 00:00:00 2001 From: RUANG Date: Wed, 20 Nov 2024 11:14:06 +0800 Subject: [PATCH 23/24] add lock to codeobject --- Objects/codeobject.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index dba43d5911da95..baa1ce82592807 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2871,20 +2871,24 @@ get_indices_in_use(PyInterpreterState *interp, struct flag_set *in_use) assert(interp->stoptheworld.world_stopped); assert(in_use->flags == NULL); int32_t max_index = 0; + INTERP_HEAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { int32_t idx = ((_PyThreadStateImpl *) p)->tlbc_index; if (idx > max_index) { max_index = idx; } } + INTERP_HEAD_UNLOCK(interp); in_use->size = (size_t) max_index + 1; in_use->flags = PyMem_Calloc(in_use->size, sizeof(*in_use->flags)); if (in_use->flags == NULL) { return -1; } + INTERP_HEAD_LOCK(interp); for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { in_use->flags[((_PyThreadStateImpl *) p)->tlbc_index] = 1; } + INTERP_HEAD_UNLOCK(interp); return 0; } From 4b9a569696aae0c8154a5eb8864df864dd366ac0 Mon Sep 17 00:00:00 2001 From: RUANG Date: Wed, 20 Nov 2024 11:19:24 +0800 Subject: [PATCH 24/24] clear ci warning --- Python/pystate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pystate.c b/Python/pystate.c index d1a74673b0ce46..fc9a72522181e9 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1848,9 +1848,9 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) { assert(tstate != NULL); PyInterpreterState *interp = tstate->interp; - _PyRuntimeState *runtime = interp->runtime; #ifdef Py_GIL_DISABLED + _PyRuntimeState *runtime = interp->runtime; assert(runtime->stoptheworld.world_stopped); #endif