From 4bd1b49c299b9df3b669c9a211600ea7fac4912f Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Sat, 4 Apr 2026 23:29:14 +0530 Subject: [PATCH 1/6] remove critical section from PyCode_Addr2Line --- Objects/codeobject.c | 17 ++++------------- Python/instrumentation.c | 30 +++++++++++++++--------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index ad1116890e23ce..e1ed6372ae99ac 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1008,13 +1008,14 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) * source location tracking (co_lines/co_positions) ******************/ -static int -_PyCode_Addr2Line(PyCodeObject *co, int addrq) +int +PyCode_Addr2Line(PyCodeObject *co, int addrq) { if (addrq < 0) { return co->co_firstlineno; } - if (co->_co_monitoring && co->_co_monitoring->lines) { + if (FT_ATOMIC_LOAD_PTR_ACQUIRE(co->_co_monitoring) && + FT_ATOMIC_LOAD_PTR_ACQUIRE(co->_co_monitoring->lines)) { return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); } assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); @@ -1040,16 +1041,6 @@ _PyCode_SafeAddr2Line(PyCodeObject *co, int addrq) return _PyCode_CheckLineNumber(addrq, &bounds); } -int -PyCode_Addr2Line(PyCodeObject *co, int addrq) -{ - int lineno; - Py_BEGIN_CRITICAL_SECTION(co); - lineno = _PyCode_Addr2Line(co, addrq); - Py_END_CRITICAL_SECTION(); - return lineno; -} - void _PyLineTable_InitAddressRange(const char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) { diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 1aed6769d217fe..8df53995cd1f5d 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1511,11 +1511,9 @@ initialize_tools(PyCodeObject *code) } static void -initialize_lines(PyCodeObject *code, int bytes_per_entry) +initialize_lines(_PyCoLineInstrumentationData *line_data, PyCodeObject *code, int bytes_per_entry) { ASSERT_WORLD_STOPPED_OR_LOCKED(code); - _PyCoLineInstrumentationData *line_data = code->_co_monitoring->lines; - assert(line_data != NULL); line_data->bytes_per_entry = bytes_per_entry; int code_len = (int)Py_SIZE(code); @@ -1635,6 +1633,7 @@ initialize_lines(PyCodeObject *code, int bytes_per_entry) set_original_opcode(line_data, handler, original_opcode); } } + FT_ATOMIC_STORE_PTR_RELEASE(code->_co_monitoring->lines, line_data); } static void @@ -1656,18 +1655,19 @@ allocate_instrumentation_data(PyCodeObject *code) ASSERT_WORLD_STOPPED_OR_LOCKED(code); if (code->_co_monitoring == NULL) { - code->_co_monitoring = PyMem_Malloc(sizeof(_PyCoMonitoringData)); - if (code->_co_monitoring == NULL) { + _PyCoMonitoringData *monitoring = PyMem_Malloc(sizeof(_PyCoMonitoringData)); + if (monitoring == NULL) { PyErr_NoMemory(); return -1; } - code->_co_monitoring->local_monitors = (_Py_LocalMonitors){ 0 }; - code->_co_monitoring->active_monitors = (_Py_LocalMonitors){ 0 }; - code->_co_monitoring->tools = NULL; - code->_co_monitoring->lines = NULL; - code->_co_monitoring->line_tools = NULL; - code->_co_monitoring->per_instruction_opcodes = NULL; - code->_co_monitoring->per_instruction_tools = NULL; + monitoring->local_monitors = (_Py_LocalMonitors){ 0 }; + monitoring->active_monitors = (_Py_LocalMonitors){ 0 }; + monitoring->tools = NULL; + monitoring->lines = NULL; + monitoring->line_tools = NULL; + monitoring->per_instruction_opcodes = NULL; + monitoring->per_instruction_tools = NULL; + FT_ATOMIC_STORE_PTR_RELEASE(code->_co_monitoring, monitoring); } return 0; } @@ -1732,12 +1732,12 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) else { bytes_per_entry = 5; } - code->_co_monitoring->lines = PyMem_Malloc(1 + code_len * bytes_per_entry); - if (code->_co_monitoring->lines == NULL) { + _PyCoLineInstrumentationData *lines = PyMem_Malloc(1 + code_len * bytes_per_entry); + if (lines == NULL) { PyErr_NoMemory(); return -1; } - initialize_lines(code, bytes_per_entry); + initialize_lines(lines, code, bytes_per_entry); } if (multitools && code->_co_monitoring->line_tools == NULL) { code->_co_monitoring->line_tools = PyMem_Malloc(code_len); From ea80076c27afa18b579eb4781e8003cacced6070 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 9 Apr 2026 19:19:19 +0530 Subject: [PATCH 2/6] address review --- Objects/codeobject.c | 9 ++++++--- Python/instrumentation.c | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e1ed6372ae99ac..17e0fb1e42cb0a 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1014,9 +1014,12 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) if (addrq < 0) { return co->co_firstlineno; } - if (FT_ATOMIC_LOAD_PTR_ACQUIRE(co->_co_monitoring) && - FT_ATOMIC_LOAD_PTR_ACQUIRE(co->_co_monitoring->lines)) { - return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + _PyCoMonitoringData *data = _Py_atomic_load_ptr_acquire(co->_co_monitoring); + if (data) { + _PyCoLineInstrumentationData *lines = _Py_atomic_load_ptr_acquire(data->lines); + if (lines) { + return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + } } assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); PyCodeAddressRange bounds; diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 8df53995cd1f5d..04e7ec682529c2 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1633,7 +1633,6 @@ initialize_lines(_PyCoLineInstrumentationData *line_data, PyCodeObject *code, in set_original_opcode(line_data, handler, original_opcode); } } - FT_ATOMIC_STORE_PTR_RELEASE(code->_co_monitoring->lines, line_data); } static void @@ -1667,7 +1666,7 @@ allocate_instrumentation_data(PyCodeObject *code) monitoring->line_tools = NULL; monitoring->per_instruction_opcodes = NULL; monitoring->per_instruction_tools = NULL; - FT_ATOMIC_STORE_PTR_RELEASE(code->_co_monitoring, monitoring); + _Py_atomic_store_ptr_release(code->_co_monitoring, monitoring); } return 0; } @@ -1738,6 +1737,7 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) return -1; } initialize_lines(lines, code, bytes_per_entry); + _Py_atomic_store_ptr_release(code->_co_monitoring->lines, line_data); } if (multitools && code->_co_monitoring->line_tools == NULL) { code->_co_monitoring->line_tools = PyMem_Malloc(code_len); From 3248964ee24a48080fbae0fe5d34950adb6296e7 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 9 Apr 2026 19:27:24 +0530 Subject: [PATCH 3/6] fix typo --- Python/instrumentation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 04e7ec682529c2..28ac7d75b8837a 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1737,7 +1737,7 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) return -1; } initialize_lines(lines, code, bytes_per_entry); - _Py_atomic_store_ptr_release(code->_co_monitoring->lines, line_data); + _Py_atomic_store_ptr_release(code->_co_monitoring->lines, lines); } if (multitools && code->_co_monitoring->line_tools == NULL) { code->_co_monitoring->line_tools = PyMem_Malloc(code_len); From d8acb4a0b53eaf2331b3593ef7bfdf067ab01a38 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 9 Apr 2026 19:36:01 +0530 Subject: [PATCH 4/6] fix another typo --- Objects/codeobject.c | 4 ++-- Python/instrumentation.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 17e0fb1e42cb0a..8f3dabd6d753a1 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1014,9 +1014,9 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) if (addrq < 0) { return co->co_firstlineno; } - _PyCoMonitoringData *data = _Py_atomic_load_ptr_acquire(co->_co_monitoring); + _PyCoMonitoringData *data = _Py_atomic_load_ptr_acquire(&co->_co_monitoring); if (data) { - _PyCoLineInstrumentationData *lines = _Py_atomic_load_ptr_acquire(data->lines); + _PyCoLineInstrumentationData *lines = _Py_atomic_load_ptr_acquire(&data->lines); if (lines) { return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); } diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 28ac7d75b8837a..3990e318f427de 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1666,7 +1666,7 @@ allocate_instrumentation_data(PyCodeObject *code) monitoring->line_tools = NULL; monitoring->per_instruction_opcodes = NULL; monitoring->per_instruction_tools = NULL; - _Py_atomic_store_ptr_release(code->_co_monitoring, monitoring); + _Py_atomic_store_ptr_release(&code->_co_monitoring, monitoring); } return 0; } @@ -1737,7 +1737,7 @@ update_instrumentation_data(PyCodeObject *code, PyInterpreterState *interp) return -1; } initialize_lines(lines, code, bytes_per_entry); - _Py_atomic_store_ptr_release(code->_co_monitoring->lines, lines); + _Py_atomic_store_ptr_release(&code->_co_monitoring->lines, lines); } if (multitools && code->_co_monitoring->line_tools == NULL) { code->_co_monitoring->line_tools = PyMem_Malloc(code_len); From 9771e79f377714d02f551cce1fcb02d3a346ad69 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:18:43 +0000 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst new file mode 100644 index 00000000000000..b0cef595129817 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-09-14-18-33.gh-issue-148037.aP3CSX.rst @@ -0,0 +1 @@ +Remove critical section from :c:func:`!PyCode_Addr2Line` in free-threading. From f02f543914d5937cadd67f6f8578e6d2fee48bca Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 10 Apr 2026 00:16:53 +0530 Subject: [PATCH 6/6] _PyCoLineInstrumentationData as argument in _Py_Instrumentation_GetLine --- Include/internal/pycore_instruments.h | 5 ++--- Objects/codeobject.c | 4 ++-- Python/instrumentation.c | 11 ++++------- Python/legacy_tracing.c | 4 ++-- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_instruments.h b/Include/internal/pycore_instruments.h index 3775b074ecf54c..1da8237e93f766 100644 --- a/Include/internal/pycore_instruments.h +++ b/Include/internal/pycore_instruments.h @@ -64,9 +64,6 @@ PyAPI_FUNC(void) _Py_call_instrumentation_exc2(PyThreadState *tstate, int event, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr, PyObject *arg0, PyObject *arg1); -extern int -_Py_Instrumentation_GetLine(PyCodeObject *code, int index); - PyAPI_DATA(PyObject) _PyInstrumentation_MISSING; PyAPI_DATA(PyObject) _PyInstrumentation_DISABLE; @@ -122,6 +119,8 @@ typedef struct _PyCoMonitoringData { uint8_t *per_instruction_tools; } _PyCoMonitoringData; +extern int +_Py_Instrumentation_GetLine(PyCodeObject *code, _PyCoLineInstrumentationData *line_data, int index); #ifdef __cplusplus } diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 8f3dabd6d753a1..f0be4a77b5468c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1018,7 +1018,7 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) if (data) { _PyCoLineInstrumentationData *lines = _Py_atomic_load_ptr_acquire(&data->lines); if (lines) { - return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + return _Py_Instrumentation_GetLine(co, lines, addrq/sizeof(_Py_CODEUNIT)); } } assert(addrq >= 0 && addrq < _PyCode_NBYTES(co)); @@ -1034,7 +1034,7 @@ _PyCode_SafeAddr2Line(PyCodeObject *co, int addrq) return co->co_firstlineno; } if (co->_co_monitoring && co->_co_monitoring->lines) { - return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT)); + return _Py_Instrumentation_GetLine(co, co->_co_monitoring->lines, addrq/sizeof(_Py_CODEUNIT)); } if (!(addrq >= 0 && addrq < _PyCode_NBYTES(co))) { return -1; diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 3990e318f427de..256e2a3d3a2df0 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -1286,13 +1286,10 @@ _Py_call_instrumentation_exc2( } int -_Py_Instrumentation_GetLine(PyCodeObject *code, int index) +_Py_Instrumentation_GetLine(PyCodeObject *code, _PyCoLineInstrumentationData *line_data, int index) { - _PyCoMonitoringData *monitoring = code->_co_monitoring; - assert(monitoring != NULL); - assert(monitoring->lines != NULL); + assert(line_data != NULL); assert(index < Py_SIZE(code)); - _PyCoLineInstrumentationData *line_data = monitoring->lines; int line_delta = get_line_delta(line_data, index); int line = compute_line(code, line_delta); return line; @@ -1310,11 +1307,11 @@ _Py_call_instrumentation_line(PyThreadState *tstate, _PyInterpreterFrame* frame, _PyCoMonitoringData *monitoring = code->_co_monitoring; _PyCoLineInstrumentationData *line_data = monitoring->lines; PyInterpreterState *interp = tstate->interp; - int line = _Py_Instrumentation_GetLine(code, i); + int line = _Py_Instrumentation_GetLine(code, line_data, i); assert(line >= 0); assert(prev != NULL); int prev_index = (int)(prev - bytecode); - int prev_line = _Py_Instrumentation_GetLine(code, prev_index); + int prev_line = _Py_Instrumentation_GetLine(code, line_data, prev_index); if (prev_line == line) { int prev_opcode = bytecode[prev_index].op.code; /* RESUME and INSTRUMENTED_RESUME are needed for the operation of diff --git a/Python/legacy_tracing.c b/Python/legacy_tracing.c index 594d5c5ead5021..bf65a904de4d21 100644 --- a/Python/legacy_tracing.c +++ b/Python/legacy_tracing.c @@ -391,8 +391,8 @@ sys_trace_jump_func( assert(PyCode_Check(code)); /* We can call _Py_Instrumentation_GetLine because we always set * line events for tracing */ - int to_line = _Py_Instrumentation_GetLine(code, to); - int from_line = _Py_Instrumentation_GetLine(code, from); + int to_line = _Py_Instrumentation_GetLine(code, code->_co_monitoring->lines, to); + int from_line = _Py_Instrumentation_GetLine(code, code->_co_monitoring->lines, from); if (to_line != from_line) { /* Will be handled by target INSTRUMENTED_LINE */ return &_PyInstrumentation_DISABLE;