Skip to content

Commit d76a24b

Browse files
Improve tracer thread safety
1 parent f8fefb3 commit d76a24b

File tree

3 files changed

+22
-10
lines changed

3 files changed

+22
-10
lines changed

Include/internal/pycore_tstate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ typedef struct _PyJitTracerInitialState {
3535
} _PyJitTracerInitialState;
3636

3737
typedef struct _PyJitTracerPreviousState {
38-
bool dependencies_still_valid;
38+
uint8_t dependencies_still_valid;
3939
bool instr_is_super;
4040
int code_max_size;
4141
int code_curr_size;
@@ -48,6 +48,7 @@ typedef struct _PyJitTracerPreviousState {
4848
} _PyJitTracerPreviousState;
4949

5050
typedef struct _PyJitTracerState {
51+
PyMutex lock;
5152
_PyUOpInstruction *code_buffer;
5253
_PyJitTracerInitialState initial_state;
5354
_PyJitTracerPreviousState prev_state;

Objects/listobject.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ ensure_shared_on_resize(PyListObject *self)
7979
// We can't use _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED here because
8080
// the `CALL_LIST_APPEND` bytecode handler may lock the list without
8181
// a critical section.
82-
assert(Py_REFCNT(self) == 1 || PyMutex_IsLocked(&_PyObject_CAST(self)->ob_mutex));
82+
assert((_Py_IsOwnedByCurrentThread((PyObject *)self) && !_PyObject_GC_IS_SHARED(self)) ||
83+
PyMutex_IsLocked(&_PyObject_CAST(self)->ob_mutex));
8384

8485
// Ensure that the list array is freed using QSBR if we are not the
8586
// owning thread.

Python/optimizer.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ _PyJit_translate_single_bytecode_to_trace(
603603
// Rewind EXTENDED_ARG so that we see the whole thing.
604604
// We must point to the first EXTENDED_ARG when deopting.
605605
int oparg = _tstate->jit_tracer_state.prev_state.instr_oparg;
606-
int opcode = this_instr->op.code;
606+
int opcode = FT_ATOMIC_LOAD_UINT8_RELAXED(this_instr->op.code);
607607
int rewind_oparg = oparg;
608608
while (rewind_oparg > 255) {
609609
rewind_oparg >>= 8;
@@ -977,17 +977,24 @@ _PyJit_TryInitializeTracing(
977977
if (oparg > 0xFFFF) {
978978
return 0;
979979
}
980+
981+
PyObject *func = PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
982+
if (func == NULL) {
983+
return 0;
984+
}
985+
986+
if (!PyMutex_LockFast(&_tstate->jit_tracer_state.lock)) {
987+
return 0;
988+
}
989+
980990
if (_tstate->jit_tracer_state.code_buffer == NULL) {
981991
_tstate->jit_tracer_state.code_buffer = (_PyUOpInstruction *)_PyObject_VirtualAlloc(UOP_BUFFER_SIZE);
982992
if (_tstate->jit_tracer_state.code_buffer == NULL) {
983993
// Don't error, just go to next instruction.
994+
PyMutex_Unlock(&_tstate->jit_tracer_state.lock);
984995
return 0;
985996
}
986997
}
987-
PyObject *func = PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
988-
if (func == NULL) {
989-
return 0;
990-
}
991998
PyCodeObject *code = _PyFrame_GetCode(frame);
992999
#ifdef Py_DEBUG
9931000
char *python_lltrace = Py_GETENV("PYTHON_LLTRACE");
@@ -1017,7 +1024,7 @@ _PyJit_TryInitializeTracing(
10171024
_tstate->jit_tracer_state.initial_state.stack_depth = curr_stackdepth;
10181025
_tstate->jit_tracer_state.initial_state.chain_depth = chain_depth;
10191026
_tstate->jit_tracer_state.prev_state.instr_frame = frame;
1020-
_tstate->jit_tracer_state.prev_state.dependencies_still_valid = true;
1027+
_tstate->jit_tracer_state.prev_state.dependencies_still_valid = 1;
10211028
_tstate->jit_tracer_state.prev_state.instr_code = (PyCodeObject *)Py_NewRef(_PyFrame_GetCode(frame));
10221029
_tstate->jit_tracer_state.prev_state.instr = curr_instr;
10231030
_tstate->jit_tracer_state.prev_state.instr_frame = frame;
@@ -1032,6 +1039,7 @@ _PyJit_TryInitializeTracing(
10321039
FT_ATOMIC_STORE_UINT16_RELAXED(close_loop_instr[1].counter.value_and_backoff, zero.value_and_backoff);
10331040
}
10341041
_Py_BloomFilter_Init(&_tstate->jit_tracer_state.prev_state.dependencies);
1042+
PyMutex_Unlock(&_tstate->jit_tracer_state.lock);
10351043
return 1;
10361044
}
10371045

@@ -1702,10 +1710,12 @@ _PyJit_Tracer_InvalidateDependency(PyThreadState *tstate, void *obj)
17021710
_Py_BloomFilter_Init(&obj_filter);
17031711
_Py_BloomFilter_Add(&obj_filter, obj);
17041712
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
1713+
PyMutex_Lock(&_tstate->jit_tracer_state.lock);
17051714
if (bloom_filter_may_contain(&_tstate->jit_tracer_state.prev_state.dependencies, &obj_filter))
17061715
{
1707-
_tstate->jit_tracer_state.prev_state.dependencies_still_valid = false;
1716+
FT_ATOMIC_STORE_UINT8(_tstate->jit_tracer_state.prev_state.dependencies_still_valid, 0);
17081717
}
1718+
PyMutex_Unlock(&_tstate->jit_tracer_state.lock);
17091719
}
17101720

17111721
void
@@ -1766,7 +1776,7 @@ _Py_Executors_InvalidateAllLockHeld(PyInterpreterState *interp, int is_invalidat
17661776
/* Clearing an executor can deallocate others, so we need to make a list of
17671777
* executors to invalidate first */
17681778
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, p) {
1769-
((_PyThreadStateImpl *)p)->jit_tracer_state.prev_state.dependencies_still_valid = false;
1779+
FT_ATOMIC_STORE_UINT8(((_PyThreadStateImpl *)p)->jit_tracer_state.prev_state.dependencies_still_valid, 0);
17701780
for (_PyExecutorObject *exec = ((_PyThreadStateImpl *)p)->jit_executor_state.executor_list_head; exec != NULL;) {
17711781
assert(exec->tstate == p);
17721782
exec->vm_data.valid = 0;

0 commit comments

Comments
 (0)