gh-114940: Add _Py_FOR_EACH_TSTATE_UNLOCKED(), and Friends by ericsnowcurrently · Pull Request #127077 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Include/internal/pycore_pystate.h
6 changes: 4 additions & 2 deletions Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2871,20 +2871,22 @@ 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;
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
int32_t idx = ((_PyThreadStateImpl *) p)->tlbc_index;
if (idx > max_index) {
max_index = idx;
}
}
_Py_FOR_EACH_TSTATE_END(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;
}
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
in_use->flags[((_PyThreadStateImpl *) p)->tlbc_index] = 1;
}
_Py_FOR_EACH_TSTATE_END(interp);
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ get_reftotal(PyInterpreterState *interp)
since we can't determine which interpreter updated it. */
Py_ssize_t total = REFTOTAL(interp);
#ifdef Py_GIL_DISABLED
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) {
/* This may race with other threads modifications to their reftotal */
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)p;
total += _Py_atomic_load_ssize_relaxed(&tstate_impl->reftotal);
Expand Down
2 changes: 1 addition & 1 deletion Objects/obmalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1405,7 +1405,7 @@ get_mimalloc_allocated_blocks(PyInterpreterState *interp)
{
size_t allocated_blocks = 0;
#ifdef Py_GIL_DISABLED
for (PyThreadState *t = interp->threads.head; t != NULL; t = t->next) {
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, t) {
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)t;
for (int i = 0; i < _Py_MIMALLOC_HEAP_COUNT; i++) {
mi_heap_t *heap = &tstate->mimalloc.heaps[i];
Expand Down
3 changes: 2 additions & 1 deletion Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,12 @@ Py_SetRecursionLimit(int new_limit)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
interp->ceval.recursion_limit = new_limit;
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
int depth = p->py_recursion_limit - p->py_recursion_remaining;
p->py_recursion_limit = new_limit;
p->py_recursion_remaining = new_limit - depth;
}
_Py_FOR_EACH_TSTATE_END(interp);
}

/* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()
Expand Down
14 changes: 4 additions & 10 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -977,25 +977,19 @@ make_pending_calls(PyThreadState *tstate)
void
_Py_set_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit)
{
_PyRuntimeState *runtime = &_PyRuntime;

HEAD_LOCK(runtime);
for (PyThreadState *tstate = interp->threads.head; tstate != NULL; tstate = tstate->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, tstate) {
_Py_set_eval_breaker_bit(tstate, bit);
}
HEAD_UNLOCK(runtime);
_Py_FOR_EACH_TSTATE_END(interp);
}

void
_Py_unset_eval_breaker_bit_all(PyInterpreterState *interp, uintptr_t bit)
{
_PyRuntimeState *runtime = &_PyRuntime;

HEAD_LOCK(runtime);
for (PyThreadState *tstate = interp->threads.head; tstate != NULL; tstate = tstate->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, tstate) {
_Py_unset_eval_breaker_bit(tstate, bit);
}
HEAD_UNLOCK(runtime);
_Py_FOR_EACH_TSTATE_END(interp);
}

void
Expand Down
25 changes: 10 additions & 15 deletions Python/gc_free_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ gc_visit_heaps_lock_held(PyInterpreterState *interp, mi_block_visit_fun *visitor
Py_ssize_t offset_pre = offset_base + 2 * sizeof(PyObject*);

// visit each thread's heaps for GC objects
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) {
struct _mimalloc_thread_state *m = &((_PyThreadStateImpl *)p)->mimalloc;
if (!_Py_atomic_load_int(&m->initialized)) {
// The thread may not have called tstate_mimalloc_bind() yet.
Expand Down Expand Up @@ -374,8 +374,7 @@ gc_visit_stackref(_PyStackRef stackref)
static void
gc_visit_thread_stacks(PyInterpreterState *interp)
{
HEAD_LOCK(&_PyRuntime);
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
for (_PyInterpreterFrame *f = p->current_frame; f != NULL; f = f->previous) {
PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable);
if (executable == NULL || !PyCode_Check(executable)) {
Expand All @@ -390,7 +389,7 @@ gc_visit_thread_stacks(PyInterpreterState *interp)
}
}
}
HEAD_UNLOCK(&_PyRuntime);
_Py_FOR_EACH_TSTATE_END(interp);
}

static void
Expand Down Expand Up @@ -429,14 +428,13 @@ 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.
HEAD_LOCK(&_PyRuntime);
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
_PyThreadStateImpl *other = (_PyThreadStateImpl *)p;
if (other != current_tstate) {
llist_concat(&current_tstate->mem_free_queue, &other->mem_free_queue);
}
}
HEAD_UNLOCK(&_PyRuntime);
_Py_FOR_EACH_TSTATE_END(interp);

_PyMem_ProcessDelayed((PyThreadState *)current_tstate);
}
Expand Down Expand Up @@ -1226,8 +1224,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
state->gcstate->old[i-1].count = 0;
}

HEAD_LOCK(&_PyRuntime);
for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p;

// merge per-thread refcount for types into the type's actual refcount
Expand All @@ -1236,7 +1233,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
// merge refcounts for all queued objects
merge_queued_objects(tstate, state);
}
HEAD_UNLOCK(&_PyRuntime);
_Py_FOR_EACH_TSTATE_END(interp);

process_delayed_frees(interp);

Expand Down Expand Up @@ -1991,13 +1988,11 @@ PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)
void
_PyGC_ClearAllFreeLists(PyInterpreterState *interp)
{
HEAD_LOCK(&_PyRuntime);
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)interp->threads.head;
while (tstate != NULL) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p;
_PyObject_ClearFreeLists(&tstate->freelists, 0);
tstate = (_PyThreadStateImpl *)tstate->base.next;
}
HEAD_UNLOCK(&_PyRuntime);
_Py_FOR_EACH_TSTATE_END(interp);
}

#endif // Py_GIL_DISABLED
7 changes: 2 additions & 5 deletions Python/instrumentation.c
Original file line number Diff line number Diff line change
Expand Up @@ -1006,13 +1006,10 @@ 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);
for (tstate = interp->threads.head; tstate;
tstate = PyThreadState_Next(tstate)) {
_Py_FOR_EACH_TSTATE_BEGIN(interp, tstate) {
set_version_raw(&tstate->eval_breaker, version);
};
HEAD_UNLOCK(runtime);
_Py_FOR_EACH_TSTATE_END(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.
Expand Down
98 changes: 46 additions & 52 deletions Python/pystate.c