Skip to content

Commit e178e42

Browse files
committed
Add lock with operations on _PyExecutorObject list
1 parent bb85af3 commit e178e42

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

Include/internal/pycore_interp_structs.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ extern "C" {
2929
# define NUM_WEAKREF_LIST_LOCKS 127
3030
#endif
3131

32+
// Executor list lock macros for thread-safe access to executor linked lists
33+
#ifdef Py_GIL_DISABLED
34+
# define EXECUTOR_LIST_LOCK(interp) \
35+
PyMutex_Lock(&(interp)->executor_list_lock)
36+
# define EXECUTOR_LIST_UNLOCK(interp) \
37+
PyMutex_Unlock(&(interp)->executor_list_lock)
38+
#else
39+
# define EXECUTOR_LIST_LOCK(interp) ((void)0)
40+
# define EXECUTOR_LIST_UNLOCK(interp) ((void)0)
41+
#endif
42+
3243
typedef int (*_Py_pending_call_func)(void *);
3344

3445
struct _pending_call {
@@ -945,6 +956,9 @@ struct _is {
945956
struct _PyExecutorObject *executor_deletion_list_head;
946957
struct _PyExecutorObject *cold_executor;
947958
int executor_deletion_list_remaining_capacity;
959+
#ifdef Py_GIL_DISABLED
960+
PyMutex executor_list_lock;
961+
#endif
948962
size_t trace_run_counter;
949963
_rare_events rare_events;
950964
PyDict_WatchCallback builtins_dict_watcher;

Python/optimizer.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ _Py_ClearExecutorDeletionList(PyInterpreterState *interp)
244244
ts = PyThreadState_Next(ts);
245245
HEAD_UNLOCK(runtime);
246246
}
247+
EXECUTOR_LIST_LOCK(interp);
247248
_PyExecutorObject **prev_to_next_ptr = &interp->executor_deletion_list_head;
248249
_PyExecutorObject *exec = *prev_to_next_ptr;
249250
while (exec != NULL) {
@@ -259,12 +260,14 @@ _Py_ClearExecutorDeletionList(PyInterpreterState *interp)
259260
exec = *prev_to_next_ptr;
260261
}
261262
interp->executor_deletion_list_remaining_capacity = EXECUTOR_DELETE_LIST_MAX;
263+
EXECUTOR_LIST_UNLOCK(interp);
262264
}
263265

264266
static void
265267
add_to_pending_deletion_list(_PyExecutorObject *self)
266268
{
267269
PyInterpreterState *interp = PyInterpreterState_Get();
270+
EXECUTOR_LIST_LOCK(interp);
268271
self->vm_data.links.next = interp->executor_deletion_list_head;
269272
interp->executor_deletion_list_head = self;
270273
if (interp->executor_deletion_list_remaining_capacity > 0) {
@@ -273,6 +276,7 @@ add_to_pending_deletion_list(_PyExecutorObject *self)
273276
else {
274277
_Py_ClearExecutorDeletionList(interp);
275278
}
279+
EXECUTOR_LIST_UNLOCK(interp);
276280
}
277281

278282
static void
@@ -1441,6 +1445,7 @@ static void
14411445
link_executor(_PyExecutorObject *executor)
14421446
{
14431447
PyInterpreterState *interp = _PyInterpreterState_GET();
1448+
EXECUTOR_LIST_LOCK(interp);
14441449
_PyExecutorLinkListNode *links = &executor->vm_data.links;
14451450
_PyExecutorObject *head = interp->executor_list_head;
14461451
if (head == NULL) {
@@ -1458,6 +1463,7 @@ link_executor(_PyExecutorObject *executor)
14581463
executor->vm_data.linked = true;
14591464
/* executor_list_head must be first in list */
14601465
assert(interp->executor_list_head->vm_data.links.previous == NULL);
1466+
EXECUTOR_LIST_UNLOCK(interp);
14611467
}
14621468

14631469
static void
@@ -1466,6 +1472,8 @@ unlink_executor(_PyExecutorObject *executor)
14661472
if (!executor->vm_data.linked) {
14671473
return;
14681474
}
1475+
PyInterpreterState *interp = PyInterpreterState_Get();
1476+
EXECUTOR_LIST_LOCK(interp);
14691477
_PyExecutorLinkListNode *links = &executor->vm_data.links;
14701478
assert(executor->vm_data.valid);
14711479
_PyExecutorObject *next = links->next;
@@ -1478,11 +1486,11 @@ unlink_executor(_PyExecutorObject *executor)
14781486
}
14791487
else {
14801488
// prev == NULL implies that executor is the list head
1481-
PyInterpreterState *interp = PyInterpreterState_Get();
14821489
assert(interp->executor_list_head == executor);
14831490
interp->executor_list_head = next;
14841491
}
14851492
executor->vm_data.linked = false;
1493+
EXECUTOR_LIST_UNLOCK(interp);
14861494
}
14871495

14881496
/* This must be called by optimizers before using the executor */
@@ -1607,16 +1615,19 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is
16071615
}
16081616
/* Clearing an executor can deallocate others, so we need to make a list of
16091617
* executors to invalidate first */
1618+
EXECUTOR_LIST_LOCK(interp);
16101619
for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) {
16111620
assert(exec->vm_data.valid);
16121621
_PyExecutorObject *next = exec->vm_data.links.next;
16131622
if (bloom_filter_may_contain(&exec->vm_data.bloom, &obj_filter) &&
16141623
PyList_Append(invalidate, (PyObject *)exec))
16151624
{
1625+
EXECUTOR_LIST_UNLOCK(interp);
16161626
goto error;
16171627
}
16181628
exec = next;
16191629
}
1630+
EXECUTOR_LIST_UNLOCK(interp);
16201631
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) {
16211632
PyObject *exec = PyList_GET_ITEM(invalidate, i);
16221633
executor_clear(exec);
@@ -1637,6 +1648,7 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is
16371648
void
16381649
_Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
16391650
{
1651+
EXECUTOR_LIST_LOCK(interp);
16401652
while (interp->executor_list_head) {
16411653
_PyExecutorObject *executor = interp->executor_list_head;
16421654
assert(executor->vm_data.valid == 1 && executor->vm_data.linked == 1);
@@ -1651,6 +1663,7 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
16511663
OPT_STAT_INC(executors_invalidated);
16521664
}
16531665
}
1666+
EXECUTOR_LIST_UNLOCK(interp);
16541667
}
16551668

16561669
void
@@ -1665,11 +1678,13 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp)
16651678

16661679
/* Clearing an executor can deallocate others, so we need to make a list of
16671680
* executors to invalidate first */
1681+
EXECUTOR_LIST_LOCK(interp);
16681682
for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) {
16691683
assert(exec->vm_data.valid);
16701684
_PyExecutorObject *next = exec->vm_data.links.next;
16711685

16721686
if (!exec->vm_data.warm && PyList_Append(invalidate, (PyObject *)exec) < 0) {
1687+
EXECUTOR_LIST_UNLOCK(interp);
16731688
goto error;
16741689
}
16751690
else {
@@ -1678,6 +1693,7 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp)
16781693

16791694
exec = next;
16801695
}
1696+
EXECUTOR_LIST_UNLOCK(interp);
16811697
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(invalidate); i++) {
16821698
PyObject *exec = PyList_GET_ITEM(invalidate, i);
16831699
executor_clear(exec);
@@ -1801,10 +1817,12 @@ _PyDumpExecutors(FILE *out)
18011817
fprintf(out, "digraph ideal {\n\n");
18021818
fprintf(out, " rankdir = \"LR\"\n\n");
18031819
PyInterpreterState *interp = PyInterpreterState_Get();
1820+
EXECUTOR_LIST_LOCK(interp);
18041821
for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) {
18051822
executor_to_gv(exec, out);
18061823
exec = exec->vm_data.links.next;
18071824
}
1825+
EXECUTOR_LIST_UNLOCK(interp);
18081826
fprintf(out, "}\n\n");
18091827
return 0;
18101828
}

Python/pystate.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,9 @@ init_interpreter(PyInterpreterState *interp,
575575
interp->executor_list_head = NULL;
576576
interp->executor_deletion_list_head = NULL;
577577
interp->executor_deletion_list_remaining_capacity = 0;
578+
#ifdef Py_GIL_DISABLED
579+
interp->executor_list_lock = (PyMutex){0};
580+
#endif
578581
interp->trace_run_counter = JIT_CLEANUP_THRESHOLD;
579582
if (interp != &runtime->_main_interpreter) {
580583
/* Fix the self-referential, statically initialized fields. */

0 commit comments

Comments
 (0)