mirror of https://github.com/python/cpython
gh-117323: Make `cell` thread-safe in free-threaded builds (#117330)
Use critical sections to lock around accesses to cell contents. The critical sections are no-ops in the default (with GIL) build.
This commit is contained in:
parent
397d88db5e
commit
19c1dd60c5
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef Py_INTERNAL_CELL_H
|
||||||
|
#define Py_INTERNAL_CELL_H
|
||||||
|
|
||||||
|
#include "pycore_critical_section.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Py_BUILD_CORE
|
||||||
|
# error "this header requires Py_BUILD_CORE define"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Sets the cell contents to `value` and return previous contents. Steals a
|
||||||
|
// reference to `value`.
|
||||||
|
static inline PyObject *
|
||||||
|
PyCell_SwapTakeRef(PyCellObject *cell, PyObject *value)
|
||||||
|
{
|
||||||
|
PyObject *old_value;
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(cell);
|
||||||
|
old_value = cell->ob_ref;
|
||||||
|
cell->ob_ref = value;
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
return old_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
PyCell_SetTakeRef(PyCellObject *cell, PyObject *value)
|
||||||
|
{
|
||||||
|
PyObject *old_value = PyCell_SwapTakeRef(cell, value);
|
||||||
|
Py_XDECREF(old_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the cell contents. Returns a new reference.
|
||||||
|
static inline PyObject *
|
||||||
|
PyCell_GetRef(PyCellObject *cell)
|
||||||
|
{
|
||||||
|
PyObject *res;
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(cell);
|
||||||
|
res = Py_XNewRef(cell->ob_ref);
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* !Py_INTERNAL_CELL_H */
|
|
@ -1130,6 +1130,7 @@ PYTHON_HEADERS= \
|
||||||
$(srcdir)/Include/internal/pycore_bytesobject.h \
|
$(srcdir)/Include/internal/pycore_bytesobject.h \
|
||||||
$(srcdir)/Include/internal/pycore_call.h \
|
$(srcdir)/Include/internal/pycore_call.h \
|
||||||
$(srcdir)/Include/internal/pycore_capsule.h \
|
$(srcdir)/Include/internal/pycore_capsule.h \
|
||||||
|
$(srcdir)/Include/internal/pycore_cell.h \
|
||||||
$(srcdir)/Include/internal/pycore_ceval.h \
|
$(srcdir)/Include/internal/pycore_ceval.h \
|
||||||
$(srcdir)/Include/internal/pycore_ceval_state.h \
|
$(srcdir)/Include/internal/pycore_ceval_state.h \
|
||||||
$(srcdir)/Include/internal/pycore_code.h \
|
$(srcdir)/Include/internal/pycore_code.h \
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* Cell object implementation */
|
/* Cell object implementation */
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
#include "pycore_cell.h" // PyCell_GetRef()
|
||||||
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
|
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
|
||||||
#include "pycore_object.h"
|
#include "pycore_object.h"
|
||||||
|
|
||||||
|
@ -56,8 +57,7 @@ PyCell_Get(PyObject *op)
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyObject *value = PyCell_GET(op);
|
return PyCell_GetRef((PyCellObject *)op);
|
||||||
return Py_XNewRef(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -67,9 +67,7 @@ PyCell_Set(PyObject *op, PyObject *value)
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
PyObject *old_value = PyCell_GET(op);
|
PyCell_SetTakeRef((PyCellObject *)op, Py_XNewRef(value));
|
||||||
PyCell_SET(op, Py_XNewRef(value));
|
|
||||||
Py_XDECREF(old_value);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,6 +210,7 @@
|
||||||
<ClInclude Include="..\Include\internal\pycore_bytesobject.h" />
|
<ClInclude Include="..\Include\internal\pycore_bytesobject.h" />
|
||||||
<ClInclude Include="..\Include\internal\pycore_call.h" />
|
<ClInclude Include="..\Include\internal\pycore_call.h" />
|
||||||
<ClInclude Include="..\Include\internal\pycore_capsule.h" />
|
<ClInclude Include="..\Include\internal\pycore_capsule.h" />
|
||||||
|
<ClInclude Include="..\Include\internal\pycore_cell.h" />
|
||||||
<ClInclude Include="..\Include\internal\pycore_ceval.h" />
|
<ClInclude Include="..\Include\internal\pycore_ceval.h" />
|
||||||
<ClInclude Include="..\Include\internal\pycore_ceval_state.h" />
|
<ClInclude Include="..\Include\internal\pycore_ceval_state.h" />
|
||||||
<ClInclude Include="..\Include\internal\pycore_cfg.h" />
|
<ClInclude Include="..\Include\internal\pycore_cfg.h" />
|
||||||
|
|
|
@ -555,6 +555,9 @@
|
||||||
<ClInclude Include="..\Include\internal\pycore_capsule.h">
|
<ClInclude Include="..\Include\internal\pycore_capsule.h">
|
||||||
<Filter>Include\internal</Filter>
|
<Filter>Include\internal</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\Include\internal\pycore_cell.h">
|
||||||
|
<Filter>Include\internal</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\Include\internal\pycore_ceval.h">
|
<ClInclude Include="..\Include\internal\pycore_ceval.h">
|
||||||
<Filter>Include\internal</Filter>
|
<Filter>Include\internal</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_abstract.h" // _PyIndex_Check()
|
#include "pycore_abstract.h" // _PyIndex_Check()
|
||||||
|
#include "pycore_cell.h" // PyCell_GetRef()
|
||||||
#include "pycore_code.h"
|
#include "pycore_code.h"
|
||||||
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
|
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
|
||||||
#include "pycore_function.h"
|
#include "pycore_function.h"
|
||||||
|
@ -1523,14 +1524,13 @@ dummy_func(
|
||||||
|
|
||||||
inst(DELETE_DEREF, (--)) {
|
inst(DELETE_DEREF, (--)) {
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyObject *cell = GETLOCAL(oparg);
|
||||||
PyObject *oldobj = PyCell_GET(cell);
|
|
||||||
// Can't use ERROR_IF here.
|
// Can't use ERROR_IF here.
|
||||||
// Fortunately we don't need its superpower.
|
// Fortunately we don't need its superpower.
|
||||||
|
PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL);
|
||||||
if (oldobj == NULL) {
|
if (oldobj == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
ERROR_NO_POP();
|
ERROR_NO_POP();
|
||||||
}
|
}
|
||||||
PyCell_SET(cell, NULL);
|
|
||||||
Py_DECREF(oldobj);
|
Py_DECREF(oldobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1543,32 +1543,28 @@ dummy_func(
|
||||||
ERROR_NO_POP();
|
ERROR_NO_POP();
|
||||||
}
|
}
|
||||||
if (!value) {
|
if (!value) {
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
value = PyCell_GET(cell);
|
value = PyCell_GetRef(cell);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
ERROR_NO_POP();
|
ERROR_NO_POP();
|
||||||
}
|
}
|
||||||
Py_INCREF(value);
|
|
||||||
}
|
}
|
||||||
Py_DECREF(class_dict);
|
Py_DECREF(class_dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
inst(LOAD_DEREF, ( -- value)) {
|
inst(LOAD_DEREF, ( -- value)) {
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
value = PyCell_GET(cell);
|
value = PyCell_GetRef(cell);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
ERROR_IF(true, error);
|
ERROR_IF(true, error);
|
||||||
}
|
}
|
||||||
Py_INCREF(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inst(STORE_DEREF, (v --)) {
|
inst(STORE_DEREF, (v --)) {
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
PyObject *oldobj = PyCell_GET(cell);
|
PyCell_SetTakeRef(cell, v);
|
||||||
PyCell_SET(cell, v);
|
|
||||||
Py_XDECREF(oldobj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inst(COPY_FREE_VARS, (--)) {
|
inst(COPY_FREE_VARS, (--)) {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_abstract.h" // _PyIndex_Check()
|
#include "pycore_abstract.h" // _PyIndex_Check()
|
||||||
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
||||||
|
#include "pycore_cell.h" // PyCell_GetRef()
|
||||||
#include "pycore_ceval.h"
|
#include "pycore_ceval.h"
|
||||||
#include "pycore_code.h"
|
#include "pycore_code.h"
|
||||||
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
|
#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
|
||||||
|
|
|
@ -1362,14 +1362,13 @@
|
||||||
case _DELETE_DEREF: {
|
case _DELETE_DEREF: {
|
||||||
oparg = CURRENT_OPARG();
|
oparg = CURRENT_OPARG();
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyObject *cell = GETLOCAL(oparg);
|
||||||
PyObject *oldobj = PyCell_GET(cell);
|
|
||||||
// Can't use ERROR_IF here.
|
// Can't use ERROR_IF here.
|
||||||
// Fortunately we don't need its superpower.
|
// Fortunately we don't need its superpower.
|
||||||
|
PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL);
|
||||||
if (oldobj == NULL) {
|
if (oldobj == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
JUMP_TO_ERROR();
|
JUMP_TO_ERROR();
|
||||||
}
|
}
|
||||||
PyCell_SET(cell, NULL);
|
|
||||||
Py_DECREF(oldobj);
|
Py_DECREF(oldobj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1387,13 +1386,12 @@
|
||||||
JUMP_TO_ERROR();
|
JUMP_TO_ERROR();
|
||||||
}
|
}
|
||||||
if (!value) {
|
if (!value) {
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
value = PyCell_GET(cell);
|
value = PyCell_GetRef(cell);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
JUMP_TO_ERROR();
|
JUMP_TO_ERROR();
|
||||||
}
|
}
|
||||||
Py_INCREF(value);
|
|
||||||
}
|
}
|
||||||
Py_DECREF(class_dict);
|
Py_DECREF(class_dict);
|
||||||
stack_pointer[-1] = value;
|
stack_pointer[-1] = value;
|
||||||
|
@ -1403,13 +1401,12 @@
|
||||||
case _LOAD_DEREF: {
|
case _LOAD_DEREF: {
|
||||||
PyObject *value;
|
PyObject *value;
|
||||||
oparg = CURRENT_OPARG();
|
oparg = CURRENT_OPARG();
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
value = PyCell_GET(cell);
|
value = PyCell_GetRef(cell);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
if (true) JUMP_TO_ERROR();
|
if (true) JUMP_TO_ERROR();
|
||||||
}
|
}
|
||||||
Py_INCREF(value);
|
|
||||||
stack_pointer[0] = value;
|
stack_pointer[0] = value;
|
||||||
stack_pointer += 1;
|
stack_pointer += 1;
|
||||||
break;
|
break;
|
||||||
|
@ -1419,10 +1416,8 @@
|
||||||
PyObject *v;
|
PyObject *v;
|
||||||
oparg = CURRENT_OPARG();
|
oparg = CURRENT_OPARG();
|
||||||
v = stack_pointer[-1];
|
v = stack_pointer[-1];
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
PyObject *oldobj = PyCell_GET(cell);
|
PyCell_SetTakeRef(cell, v);
|
||||||
PyCell_SET(cell, v);
|
|
||||||
Py_XDECREF(oldobj);
|
|
||||||
stack_pointer += -1;
|
stack_pointer += -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2320,14 +2320,13 @@
|
||||||
next_instr += 1;
|
next_instr += 1;
|
||||||
INSTRUCTION_STATS(DELETE_DEREF);
|
INSTRUCTION_STATS(DELETE_DEREF);
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyObject *cell = GETLOCAL(oparg);
|
||||||
PyObject *oldobj = PyCell_GET(cell);
|
|
||||||
// Can't use ERROR_IF here.
|
// Can't use ERROR_IF here.
|
||||||
// Fortunately we don't need its superpower.
|
// Fortunately we don't need its superpower.
|
||||||
|
PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL);
|
||||||
if (oldobj == NULL) {
|
if (oldobj == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
PyCell_SET(cell, NULL);
|
|
||||||
Py_DECREF(oldobj);
|
Py_DECREF(oldobj);
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
@ -4096,13 +4095,12 @@
|
||||||
next_instr += 1;
|
next_instr += 1;
|
||||||
INSTRUCTION_STATS(LOAD_DEREF);
|
INSTRUCTION_STATS(LOAD_DEREF);
|
||||||
PyObject *value;
|
PyObject *value;
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
value = PyCell_GET(cell);
|
value = PyCell_GetRef(cell);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
if (true) goto error;
|
if (true) goto error;
|
||||||
}
|
}
|
||||||
Py_INCREF(value);
|
|
||||||
stack_pointer[0] = value;
|
stack_pointer[0] = value;
|
||||||
stack_pointer += 1;
|
stack_pointer += 1;
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
@ -4186,13 +4184,12 @@
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (!value) {
|
if (!value) {
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
value = PyCell_GET(cell);
|
value = PyCell_GetRef(cell);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
_PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
Py_INCREF(value);
|
|
||||||
}
|
}
|
||||||
Py_DECREF(class_dict);
|
Py_DECREF(class_dict);
|
||||||
stack_pointer[-1] = value;
|
stack_pointer[-1] = value;
|
||||||
|
@ -5436,10 +5433,8 @@
|
||||||
INSTRUCTION_STATS(STORE_DEREF);
|
INSTRUCTION_STATS(STORE_DEREF);
|
||||||
PyObject *v;
|
PyObject *v;
|
||||||
v = stack_pointer[-1];
|
v = stack_pointer[-1];
|
||||||
PyObject *cell = GETLOCAL(oparg);
|
PyCellObject *cell = (PyCellObject *)GETLOCAL(oparg);
|
||||||
PyObject *oldobj = PyCell_GET(cell);
|
PyCell_SetTakeRef(cell, v);
|
||||||
PyCell_SET(cell, v);
|
|
||||||
Py_XDECREF(oldobj);
|
|
||||||
stack_pointer += -1;
|
stack_pointer += -1;
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
|
@ -520,8 +520,9 @@ def effect_depends_on_oparg_1(op: parser.InstDef) -> bool:
|
||||||
def compute_properties(op: parser.InstDef) -> Properties:
|
def compute_properties(op: parser.InstDef) -> Properties:
|
||||||
has_free = (
|
has_free = (
|
||||||
variable_used(op, "PyCell_New")
|
variable_used(op, "PyCell_New")
|
||||||
or variable_used(op, "PyCell_GET")
|
or variable_used(op, "PyCell_GetRef")
|
||||||
or variable_used(op, "PyCell_SET")
|
or variable_used(op, "PyCell_SetTakeRef")
|
||||||
|
or variable_used(op, "PyCell_SwapTakeRef")
|
||||||
)
|
)
|
||||||
deopts_if = variable_used(op, "DEOPT_IF")
|
deopts_if = variable_used(op, "DEOPT_IF")
|
||||||
exits_if = variable_used(op, "EXIT_IF")
|
exits_if = variable_used(op, "EXIT_IF")
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "pycore_call.h"
|
#include "pycore_call.h"
|
||||||
#include "pycore_ceval.h"
|
#include "pycore_ceval.h"
|
||||||
|
#include "pycore_cell.h"
|
||||||
#include "pycore_dict.h"
|
#include "pycore_dict.h"
|
||||||
#include "pycore_emscripten_signal.h"
|
#include "pycore_emscripten_signal.h"
|
||||||
#include "pycore_intrinsics.h"
|
#include "pycore_intrinsics.h"
|
||||||
|
|
Loading…
Reference in New Issue