https://github.com/python/cpython/commit/4f325168048fda89cef8bd2de859f65ec91754a3 commit: 4f325168048fda89cef8bd2de859f65ec91754a3 branch: main author: Sam Gross <colesb...@gmail.com> committer: colesbury <colesb...@gmail.com> date: 2025-03-21T11:10:07-04:00 summary:
gh-128421: Add locking to most frame object functions (gh-131479) This makes more operations on frame objects thread-safe in the free threaded build, which fixes some data races that occurred when passing exceptions between threads. However, accessing local variables from another thread while its running is still not thread-safe and may crash the interpreter. files: A Objects/clinic/frameobject.c.h M Objects/frameobject.c M Python/frame.c diff --git a/Objects/clinic/frameobject.c.h b/Objects/clinic/frameobject.c.h new file mode 100644 index 00000000000000..327896f4b97c68 --- /dev/null +++ b/Objects/clinic/frameobject.c.h @@ -0,0 +1,436 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() + +PyDoc_STRVAR(frame_locals__doc__, +"Return the mapping used by the frame to look up local variables."); +#if defined(frame_locals_DOCSTR) +# undef frame_locals_DOCSTR +#endif +#define frame_locals_DOCSTR frame_locals__doc__ + +#if !defined(frame_locals_DOCSTR) +# define frame_locals_DOCSTR NULL +#endif +#if defined(FRAME_LOCALS_GETSETDEF) +# undef FRAME_LOCALS_GETSETDEF +# define FRAME_LOCALS_GETSETDEF {"f_locals", (getter)frame_locals_get, (setter)frame_locals_set, frame_locals_DOCSTR}, +#else +# define FRAME_LOCALS_GETSETDEF {"f_locals", (getter)frame_locals_get, NULL, frame_locals_DOCSTR}, +#endif + +static PyObject * +frame_locals_get_impl(PyFrameObject *self); + +static PyObject * +frame_locals_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_locals_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_lineno__doc__, +"Return the current line number in the frame."); +#if defined(frame_lineno_DOCSTR) +# undef frame_lineno_DOCSTR +#endif +#define frame_lineno_DOCSTR frame_lineno__doc__ + +#if !defined(frame_lineno_DOCSTR) +# define frame_lineno_DOCSTR NULL +#endif +#if defined(FRAME_LINENO_GETSETDEF) +# undef FRAME_LINENO_GETSETDEF +# define FRAME_LINENO_GETSETDEF {"f_lineno", (getter)frame_lineno_get, (setter)frame_lineno_set, frame_lineno_DOCSTR}, +#else +# define FRAME_LINENO_GETSETDEF {"f_lineno", (getter)frame_lineno_get, NULL, frame_lineno_DOCSTR}, +#endif + +static PyObject * +frame_lineno_get_impl(PyFrameObject *self); + +static PyObject * +frame_lineno_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_lineno_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_lasti__doc__, +"Return the index of the last attempted instruction in the frame."); +#if defined(frame_lasti_DOCSTR) +# undef frame_lasti_DOCSTR +#endif +#define frame_lasti_DOCSTR frame_lasti__doc__ + +#if !defined(frame_lasti_DOCSTR) +# define frame_lasti_DOCSTR NULL +#endif +#if defined(FRAME_LASTI_GETSETDEF) +# undef FRAME_LASTI_GETSETDEF +# define FRAME_LASTI_GETSETDEF {"f_lasti", (getter)frame_lasti_get, (setter)frame_lasti_set, frame_lasti_DOCSTR}, +#else +# define FRAME_LASTI_GETSETDEF {"f_lasti", (getter)frame_lasti_get, NULL, frame_lasti_DOCSTR}, +#endif + +static PyObject * +frame_lasti_get_impl(PyFrameObject *self); + +static PyObject * +frame_lasti_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_lasti_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_globals__doc__, +"Return the global variables in the frame."); +#if defined(frame_globals_DOCSTR) +# undef frame_globals_DOCSTR +#endif +#define frame_globals_DOCSTR frame_globals__doc__ + +#if !defined(frame_globals_DOCSTR) +# define frame_globals_DOCSTR NULL +#endif +#if defined(FRAME_GLOBALS_GETSETDEF) +# undef FRAME_GLOBALS_GETSETDEF +# define FRAME_GLOBALS_GETSETDEF {"f_globals", (getter)frame_globals_get, (setter)frame_globals_set, frame_globals_DOCSTR}, +#else +# define FRAME_GLOBALS_GETSETDEF {"f_globals", (getter)frame_globals_get, NULL, frame_globals_DOCSTR}, +#endif + +static PyObject * +frame_globals_get_impl(PyFrameObject *self); + +static PyObject * +frame_globals_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_globals_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_builtins__doc__, +"Return the built-in variables in the frame."); +#if defined(frame_builtins_DOCSTR) +# undef frame_builtins_DOCSTR +#endif +#define frame_builtins_DOCSTR frame_builtins__doc__ + +#if !defined(frame_builtins_DOCSTR) +# define frame_builtins_DOCSTR NULL +#endif +#if defined(FRAME_BUILTINS_GETSETDEF) +# undef FRAME_BUILTINS_GETSETDEF +# define FRAME_BUILTINS_GETSETDEF {"f_builtins", (getter)frame_builtins_get, (setter)frame_builtins_set, frame_builtins_DOCSTR}, +#else +# define FRAME_BUILTINS_GETSETDEF {"f_builtins", (getter)frame_builtins_get, NULL, frame_builtins_DOCSTR}, +#endif + +static PyObject * +frame_builtins_get_impl(PyFrameObject *self); + +static PyObject * +frame_builtins_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_builtins_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_code__doc__, +"Return the code object being executed in this frame."); +#if defined(frame_code_DOCSTR) +# undef frame_code_DOCSTR +#endif +#define frame_code_DOCSTR frame_code__doc__ + +#if !defined(frame_code_DOCSTR) +# define frame_code_DOCSTR NULL +#endif +#if defined(FRAME_CODE_GETSETDEF) +# undef FRAME_CODE_GETSETDEF +# define FRAME_CODE_GETSETDEF {"f_code", (getter)frame_code_get, (setter)frame_code_set, frame_code_DOCSTR}, +#else +# define FRAME_CODE_GETSETDEF {"f_code", (getter)frame_code_get, NULL, frame_code_DOCSTR}, +#endif + +static PyObject * +frame_code_get_impl(PyFrameObject *self); + +static PyObject * +frame_code_get(PyObject *self, void *Py_UNUSED(context)) +{ + return frame_code_get_impl((PyFrameObject *)self); +} + +#if !defined(frame_back_DOCSTR) +# define frame_back_DOCSTR NULL +#endif +#if defined(FRAME_BACK_GETSETDEF) +# undef FRAME_BACK_GETSETDEF +# define FRAME_BACK_GETSETDEF {"f_back", (getter)frame_back_get, (setter)frame_back_set, frame_back_DOCSTR}, +#else +# define FRAME_BACK_GETSETDEF {"f_back", (getter)frame_back_get, NULL, frame_back_DOCSTR}, +#endif + +static PyObject * +frame_back_get_impl(PyFrameObject *self); + +static PyObject * +frame_back_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_back_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_trace_opcodes__doc__, +"Return True if opcode tracing is enabled, False otherwise."); +#if defined(frame_trace_opcodes_DOCSTR) +# undef frame_trace_opcodes_DOCSTR +#endif +#define frame_trace_opcodes_DOCSTR frame_trace_opcodes__doc__ + +#if !defined(frame_trace_opcodes_DOCSTR) +# define frame_trace_opcodes_DOCSTR NULL +#endif +#if defined(FRAME_TRACE_OPCODES_GETSETDEF) +# undef FRAME_TRACE_OPCODES_GETSETDEF +# define FRAME_TRACE_OPCODES_GETSETDEF {"f_trace_opcodes", (getter)frame_trace_opcodes_get, (setter)frame_trace_opcodes_set, frame_trace_opcodes_DOCSTR}, +#else +# define FRAME_TRACE_OPCODES_GETSETDEF {"f_trace_opcodes", (getter)frame_trace_opcodes_get, NULL, frame_trace_opcodes_DOCSTR}, +#endif + +static PyObject * +frame_trace_opcodes_get_impl(PyFrameObject *self); + +static PyObject * +frame_trace_opcodes_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_trace_opcodes_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(frame_trace_opcodes_DOCSTR) +# define frame_trace_opcodes_DOCSTR NULL +#endif +#if defined(FRAME_TRACE_OPCODES_GETSETDEF) +# undef FRAME_TRACE_OPCODES_GETSETDEF +# define FRAME_TRACE_OPCODES_GETSETDEF {"f_trace_opcodes", (getter)frame_trace_opcodes_get, (setter)frame_trace_opcodes_set, frame_trace_opcodes_DOCSTR}, +#else +# define FRAME_TRACE_OPCODES_GETSETDEF {"f_trace_opcodes", NULL, (setter)frame_trace_opcodes_set, NULL}, +#endif + +static int +frame_trace_opcodes_set_impl(PyFrameObject *self, PyObject *value); + +static int +frame_trace_opcodes_set(PyObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_trace_opcodes_set_impl((PyFrameObject *)self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(frame_lineno_DOCSTR) +# define frame_lineno_DOCSTR NULL +#endif +#if defined(FRAME_LINENO_GETSETDEF) +# undef FRAME_LINENO_GETSETDEF +# define FRAME_LINENO_GETSETDEF {"f_lineno", (getter)frame_lineno_get, (setter)frame_lineno_set, frame_lineno_DOCSTR}, +#else +# define FRAME_LINENO_GETSETDEF {"f_lineno", NULL, (setter)frame_lineno_set, NULL}, +#endif + +static int +frame_lineno_set_impl(PyFrameObject *self, PyObject *value); + +static int +frame_lineno_set(PyObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_lineno_set_impl((PyFrameObject *)self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_trace__doc__, +"Return the trace function for this frame, or None if no trace function is set."); +#if defined(frame_trace_DOCSTR) +# undef frame_trace_DOCSTR +#endif +#define frame_trace_DOCSTR frame_trace__doc__ + +#if !defined(frame_trace_DOCSTR) +# define frame_trace_DOCSTR NULL +#endif +#if defined(FRAME_TRACE_GETSETDEF) +# undef FRAME_TRACE_GETSETDEF +# define FRAME_TRACE_GETSETDEF {"f_trace", (getter)frame_trace_get, (setter)frame_trace_set, frame_trace_DOCSTR}, +#else +# define FRAME_TRACE_GETSETDEF {"f_trace", (getter)frame_trace_get, NULL, frame_trace_DOCSTR}, +#endif + +static PyObject * +frame_trace_get_impl(PyFrameObject *self); + +static PyObject * +frame_trace_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_trace_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(frame_trace_DOCSTR) +# define frame_trace_DOCSTR NULL +#endif +#if defined(FRAME_TRACE_GETSETDEF) +# undef FRAME_TRACE_GETSETDEF +# define FRAME_TRACE_GETSETDEF {"f_trace", (getter)frame_trace_get, (setter)frame_trace_set, frame_trace_DOCSTR}, +#else +# define FRAME_TRACE_GETSETDEF {"f_trace", NULL, (setter)frame_trace_set, NULL}, +#endif + +static int +frame_trace_set_impl(PyFrameObject *self, PyObject *value); + +static int +frame_trace_set(PyObject *self, PyObject *value, void *Py_UNUSED(context)) +{ + int return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_trace_set_impl((PyFrameObject *)self, value); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_generator__doc__, +"Return the generator or coroutine associated with this frame, or None."); +#if defined(frame_generator_DOCSTR) +# undef frame_generator_DOCSTR +#endif +#define frame_generator_DOCSTR frame_generator__doc__ + +#if !defined(frame_generator_DOCSTR) +# define frame_generator_DOCSTR NULL +#endif +#if defined(FRAME_GENERATOR_GETSETDEF) +# undef FRAME_GENERATOR_GETSETDEF +# define FRAME_GENERATOR_GETSETDEF {"f_generator", (getter)frame_generator_get, (setter)frame_generator_set, frame_generator_DOCSTR}, +#else +# define FRAME_GENERATOR_GETSETDEF {"f_generator", (getter)frame_generator_get, NULL, frame_generator_DOCSTR}, +#endif + +static PyObject * +frame_generator_get_impl(PyFrameObject *self); + +static PyObject * +frame_generator_get(PyObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_generator_get_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame_clear__doc__, +"clear($self, /)\n" +"--\n" +"\n" +"Clear all references held by the frame."); + +#define FRAME_CLEAR_METHODDEF \ + {"clear", (PyCFunction)frame_clear, METH_NOARGS, frame_clear__doc__}, + +static PyObject * +frame_clear_impl(PyFrameObject *self); + +static PyObject * +frame_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame_clear_impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +PyDoc_STRVAR(frame___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n" +"Return the size of the frame in memory, in bytes."); + +#define FRAME___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)frame___sizeof__, METH_NOARGS, frame___sizeof____doc__}, + +static PyObject * +frame___sizeof___impl(PyFrameObject *self); + +static PyObject * +frame___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = frame___sizeof___impl((PyFrameObject *)self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} +/*[clinic end generated code: output=74abf652547c0c11 input=a9049054013a1b77]*/ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 665199f7436ee8..88893467855ba2 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -19,6 +19,8 @@ #include "pycore_frame.h" #include "opcode.h" // EXTENDED_ARG +#include "clinic/frameobject.c.h" + #define PyFrameObject_CAST(op) \ (assert(PyObject_TypeCheck((op), &PyFrame_Type)), (PyFrameObject *)(op)) @@ -30,6 +32,11 @@ #define OFF(x) offsetof(PyFrameObject, x) +/*[clinic input] +class frame "PyFrameObject *" "&PyFrame_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2d1dbf2e06cf351f]*/ + // Returns new reference or NULL static PyObject * @@ -913,32 +920,36 @@ static PyMemberDef frame_memberlist[] = { {NULL} /* Sentinel */ }; +/*[clinic input] +@critical_section +@getter +frame.f_locals as frame_locals + +Return the mapping used by the frame to look up local variables. +[clinic start generated code]*/ + static PyObject * -frame_getlocals(PyObject *op, void *Py_UNUSED(closure)) +frame_locals_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=b4ace8bb4cae71f4 input=7bd444d0dc8ddf44]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - if (f == NULL) { - PyErr_BadInternalCall(); - return NULL; - } - assert(!_PyFrame_IsIncomplete(f->f_frame)); + assert(!_PyFrame_IsIncomplete(self->f_frame)); - PyCodeObject *co = _PyFrame_GetCode(f->f_frame); + PyCodeObject *co = _PyFrame_GetCode(self->f_frame); - if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(f->f_frame)) { - if (f->f_frame->f_locals == NULL) { + if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(self->f_frame)) { + if (self->f_frame->f_locals == NULL) { // We found cases when f_locals is NULL for non-optimized code. // We fill the f_locals with an empty dict to avoid crash until // we find the root cause. - f->f_frame->f_locals = PyDict_New(); - if (f->f_frame->f_locals == NULL) { + self->f_frame->f_locals = PyDict_New(); + if (self->f_frame->f_locals == NULL) { return NULL; } } - return Py_NewRef(f->f_frame->f_locals); + return Py_NewRef(self->f_frame->f_locals); } - return _PyFrameLocalsProxy_New(f); + return _PyFrameLocalsProxy_New(self); } int @@ -961,99 +972,155 @@ PyFrame_GetLineNumber(PyFrameObject *f) return PyUnstable_InterpreterFrame_GetLine(f->f_frame); } +/*[clinic input] +@critical_section +@getter +frame.f_lineno as frame_lineno + +Return the current line number in the frame. +[clinic start generated code]*/ + static PyObject * -frame_getlineno(PyObject *op, void *Py_UNUSED(closure)) +frame_lineno_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=70f35de5ac7ad630 input=87b9ec648b742936]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - int lineno = PyFrame_GetLineNumber(f); + int lineno = PyFrame_GetLineNumber(self); if (lineno < 0) { Py_RETURN_NONE; } - else { - return PyLong_FromLong(lineno); - } + return PyLong_FromLong(lineno); } +/*[clinic input] +@critical_section +@getter +frame.f_lasti as frame_lasti + +Return the index of the last attempted instruction in the frame. +[clinic start generated code]*/ + static PyObject * -frame_getlasti(PyObject *op, void *Py_UNUSED(closure)) +frame_lasti_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=03275b4f0327d1a2 input=0225ed49cb1fbeeb]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - int lasti = _PyInterpreterFrame_LASTI(f->f_frame); + int lasti = _PyInterpreterFrame_LASTI(self->f_frame); if (lasti < 0) { return PyLong_FromLong(-1); } return PyLong_FromLong(lasti * sizeof(_Py_CODEUNIT)); } +/*[clinic input] +@critical_section +@getter +frame.f_globals as frame_globals + +Return the global variables in the frame. +[clinic start generated code]*/ + static PyObject * -frame_getglobals(PyObject *op, void *Py_UNUSED(closure)) +frame_globals_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=7758788c32885528 input=7fff7241357d314d]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - PyObject *globals = f->f_frame->f_globals; + PyObject *globals = self->f_frame->f_globals; if (globals == NULL) { globals = Py_None; } return Py_NewRef(globals); } +/*[clinic input] +@critical_section +@getter +frame.f_builtins as frame_builtins + +Return the built-in variables in the frame. +[clinic start generated code]*/ + static PyObject * -frame_getbuiltins(PyObject *op, void *Py_UNUSED(closure)) +frame_builtins_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=45362faa6d42c702 input=27c696d6ffcad2c7]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - PyObject *builtins = f->f_frame->f_builtins; + PyObject *builtins = self->f_frame->f_builtins; if (builtins == NULL) { builtins = Py_None; } return Py_NewRef(builtins); } +/*[clinic input] +@getter +frame.f_code as frame_code + +Return the code object being executed in this frame. +[clinic start generated code]*/ + static PyObject * -frame_getcode(PyObject *op, void *Py_UNUSED(closure)) +frame_code_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=a5ed6207395a8cef input=e127e7098c124816]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - if (PySys_Audit("object.__getattr__", "Os", f, "f_code") < 0) { + if (PySys_Audit("object.__getattr__", "Os", self, "f_code") < 0) { return NULL; } - return (PyObject *)PyFrame_GetCode(f); + return (PyObject *)PyFrame_GetCode(self); } +/*[clinic input] +@critical_section +@getter +frame.f_back as frame_back +[clinic start generated code]*/ + static PyObject * -frame_getback(PyObject *op, void *Py_UNUSED(closure)) +frame_back_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=3a84c22a55a63c79 input=9e528570d0e1f44a]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - PyObject *res = (PyObject *)PyFrame_GetBack(f); + PyObject *res = (PyObject *)PyFrame_GetBack(self); if (res == NULL) { Py_RETURN_NONE; } return res; } +/*[clinic input] +@critical_section +@getter +frame.f_trace_opcodes as frame_trace_opcodes + +Return True if opcode tracing is enabled, False otherwise. +[clinic start generated code]*/ + static PyObject * -frame_gettrace_opcodes(PyObject *op, void *Py_UNUSED(closure)) +frame_trace_opcodes_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=53ff41d09cc32e87 input=4eb91dc88e04677a]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - PyObject *result = f->f_trace_opcodes ? Py_True : Py_False; - return Py_NewRef(result); + return self->f_trace_opcodes ? Py_True : Py_False; } +/*[clinic input] +@critical_section +@setter +frame.f_trace_opcodes as frame_trace_opcodes +[clinic start generated code]*/ + static int -frame_settrace_opcodes(PyObject *op, PyObject* value, void *Py_UNUSED(closure)) +frame_trace_opcodes_set_impl(PyFrameObject *self, PyObject *value) +/*[clinic end generated code: output=92619da2bfccd449 input=7e286eea3c0333ff]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); if (!PyBool_Check(value)) { PyErr_SetString(PyExc_TypeError, "attribute value type must be bool"); return -1; } if (value == Py_True) { - f->f_trace_opcodes = 1; - if (f->f_trace) { - return _PyEval_SetOpcodeTrace(f, true); + self->f_trace_opcodes = 1; + if (self->f_trace) { + return _PyEval_SetOpcodeTrace(self, true); } } else { - f->f_trace_opcodes = 0; - return _PyEval_SetOpcodeTrace(f, false); + self->f_trace_opcodes = 0; + return _PyEval_SetOpcodeTrace(self, false); } return 0; } @@ -1530,23 +1597,29 @@ static bool frame_is_suspended(PyFrameObject *frame) * 'return' or 'exception' event since the eval loop has been exited at * that time. */ +/*[clinic input] +@critical_section +@setter +frame.f_lineno as frame_lineno +[clinic start generated code]*/ + static int -frame_setlineno(PyObject *op, PyObject* p_new_lineno, void *Py_UNUSED(closure)) +frame_lineno_set_impl(PyFrameObject *self, PyObject *value) +/*[clinic end generated code: output=e64c86ff6be64292 input=36ed3c896b27fb91]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - PyCodeObject *code = _PyFrame_GetCode(f->f_frame); - if (p_new_lineno == NULL) { + PyCodeObject *code = _PyFrame_GetCode(self->f_frame); + if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "cannot delete attribute"); return -1; } /* f_lineno must be an integer. */ - if (!PyLong_CheckExact(p_new_lineno)) { + if (!PyLong_CheckExact(value)) { PyErr_SetString(PyExc_ValueError, "lineno must be an integer"); return -1; } - bool is_suspended = frame_is_suspended(f); + bool is_suspended = frame_is_suspended(self); /* * This code preserves the historical restrictions on * setting the line number of a frame. @@ -1599,7 +1672,7 @@ frame_setlineno(PyObject *op, PyObject* p_new_lineno, void *Py_UNUSED(closure)) /* Fail if the line falls outside the code block and select first line with actual code. */ int overflow; - long l_new_lineno = PyLong_AsLongAndOverflow(p_new_lineno, &overflow); + long l_new_lineno = PyLong_AsLongAndOverflow(value, &overflow); if (overflow #if SIZEOF_LONG > SIZEOF_INT || l_new_lineno > INT_MAX @@ -1644,7 +1717,7 @@ frame_setlineno(PyObject *op, PyObject* p_new_lineno, void *Py_UNUSED(closure)) int64_t best_stack = OVERFLOWED; int best_addr = -1; - int64_t start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)]; + int64_t start_stack = stacks[_PyInterpreterFrame_LASTI(self->f_frame)]; int err = -1; const char *msg = "cannot find bytecode for specified line"; for (int i = 0; i < len; i++) { @@ -1684,7 +1757,7 @@ frame_setlineno(PyObject *op, PyObject* p_new_lineno, void *Py_UNUSED(closure)) for (int i = 0; i < code->co_nlocalsplus; i++) { // Counting every unbound local is overly-cautious, but a full flow // analysis (like we do in the compiler) is probably too expensive: - unbound += PyStackRef_IsNull(f->f_frame->localsplus[i]); + unbound += PyStackRef_IsNull(self->f_frame->localsplus[i]); } if (unbound) { const char *e = "assigning None to %d unbound local%s"; @@ -1695,8 +1768,8 @@ frame_setlineno(PyObject *op, PyObject* p_new_lineno, void *Py_UNUSED(closure)) // Do this in a second pass to avoid writing a bunch of Nones when // warnings are being treated as errors and the previous bit raises: for (int i = 0; i < code->co_nlocalsplus; i++) { - if (PyStackRef_IsNull(f->f_frame->localsplus[i])) { - f->f_frame->localsplus[i] = PyStackRef_None; + if (PyStackRef_IsNull(self->f_frame->localsplus[i])) { + self->f_frame->localsplus[i] = PyStackRef_None; unbound--; } } @@ -1709,53 +1782,77 @@ frame_setlineno(PyObject *op, PyObject* p_new_lineno, void *Py_UNUSED(closure)) while (start_stack > best_stack) { if (top_of_stack(start_stack) == Except) { /* Pop exception stack as well as the evaluation stack */ - PyObject *exc = PyStackRef_AsPyObjectBorrow(_PyFrame_StackPop(f->f_frame)); + PyObject *exc = PyStackRef_AsPyObjectBorrow(_PyFrame_StackPop(self->f_frame)); assert(PyExceptionInstance_Check(exc) || exc == Py_None); PyThreadState *tstate = _PyThreadState_GET(); Py_XSETREF(tstate->exc_info->exc_value, exc == Py_None ? NULL : exc); } else { - PyStackRef_XCLOSE(_PyFrame_StackPop(f->f_frame)); + PyStackRef_XCLOSE(_PyFrame_StackPop(self->f_frame)); } start_stack = pop_value(start_stack); } /* Finally set the new lasti and return OK. */ - f->f_lineno = 0; - f->f_frame->instr_ptr = _PyFrame_GetBytecode(f->f_frame) + best_addr; + self->f_lineno = 0; + self->f_frame->instr_ptr = _PyFrame_GetBytecode(self->f_frame) + best_addr; return 0; } +/*[clinic input] +@critical_section +@getter +frame.f_trace as frame_trace + +Return the trace function for this frame, or None if no trace function is set. +[clinic start generated code]*/ + static PyObject * -frame_gettrace(PyObject *op, void *Py_UNUSED(closure)) +frame_trace_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=5475cbfce07826cd input=f382612525829773]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - PyObject* trace = f->f_trace; - if (trace == NULL) + PyObject* trace = self->f_trace; + if (trace == NULL) { trace = Py_None; + } return Py_NewRef(trace); } +/*[clinic input] +@critical_section +@setter +frame.f_trace as frame_trace +[clinic start generated code]*/ + static int -frame_settrace(PyObject *op, PyObject* v, void *Py_UNUSED(closure)) +frame_trace_set_impl(PyFrameObject *self, PyObject *value) +/*[clinic end generated code: output=d6fe08335cf76ae4 input=d96a18bda085707f]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - if (v == Py_None) { - v = NULL; + if (value == Py_None) { + value = NULL; } - if (v != f->f_trace) { - Py_XSETREF(f->f_trace, Py_XNewRef(v)); - if (v != NULL && f->f_trace_opcodes) { - return _PyEval_SetOpcodeTrace(f, true); + if (value != self->f_trace) { + Py_XSETREF(self->f_trace, Py_XNewRef(value)); + if (value != NULL && self->f_trace_opcodes) { + return _PyEval_SetOpcodeTrace(self, true); } } return 0; } +/*[clinic input] +@critical_section +@getter +frame.f_generator as frame_generator + +Return the generator or coroutine associated with this frame, or None. +[clinic start generated code]*/ + static PyObject * -frame_getgenerator(PyObject *op, void *Py_UNUSED(closure)) { - PyFrameObject *f = PyFrameObject_CAST(op); - if (f->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { - PyObject *gen = (PyObject *)_PyGen_GetGeneratorFromFrame(f->f_frame); +frame_generator_get_impl(PyFrameObject *self) +/*[clinic end generated code: output=97aeb2392562e55b input=00a2bd008b239ab0]*/ +{ + if (self->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { + PyObject *gen = (PyObject *)_PyGen_GetGeneratorFromFrame(self->f_frame); return Py_NewRef(gen); } Py_RETURN_NONE; @@ -1763,16 +1860,16 @@ frame_getgenerator(PyObject *op, void *Py_UNUSED(closure)) { static PyGetSetDef frame_getsetlist[] = { - {"f_back", frame_getback, NULL, NULL}, - {"f_locals", frame_getlocals, NULL, NULL}, - {"f_lineno", frame_getlineno, frame_setlineno, NULL}, - {"f_trace", frame_gettrace, frame_settrace, NULL}, - {"f_lasti", frame_getlasti, NULL, NULL}, - {"f_globals", frame_getglobals, NULL, NULL}, - {"f_builtins", frame_getbuiltins, NULL, NULL}, - {"f_code", frame_getcode, NULL, NULL}, - {"f_trace_opcodes", frame_gettrace_opcodes, frame_settrace_opcodes, NULL}, - {"f_generator", frame_getgenerator, NULL, NULL}, + FRAME_BACK_GETSETDEF + FRAME_LOCALS_GETSETDEF + FRAME_LINENO_GETSETDEF + FRAME_TRACE_GETSETDEF + FRAME_LASTI_GETSETDEF + FRAME_GLOBALS_GETSETDEF + FRAME_BUILTINS_GETSETDEF + FRAME_CODE_GETSETDEF + FRAME_TRACE_OPCODES_GETSETDEF + FRAME_GENERATOR_GETSETDEF {0} }; @@ -1849,12 +1946,19 @@ frame_tp_clear(PyObject *op) return 0; } +/*[clinic input] +@critical_section +frame.clear + +Clear all references held by the frame. +[clinic start generated code]*/ + static PyObject * -frame_clear(PyObject *op, PyObject *Py_UNUSED(ignored)) +frame_clear_impl(PyFrameObject *self) +/*[clinic end generated code: output=864c662f16e9bfcc input=c358f9cff5f9b681]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); - if (f->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { - PyGenObject *gen = _PyGen_GetGeneratorFromFrame(f->f_frame); + if (self->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { + PyGenObject *gen = _PyGen_GetGeneratorFromFrame(self->f_frame); if (gen->gi_frame_state == FRAME_EXECUTING) { goto running; } @@ -1863,12 +1967,12 @@ frame_clear(PyObject *op, PyObject *Py_UNUSED(ignored)) } _PyGen_Finalize((PyObject *)gen); } - else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) { + else if (self->f_frame->owner == FRAME_OWNED_BY_THREAD) { goto running; } else { - assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); - (void)frame_tp_clear(op); + assert(self->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); + (void)frame_tp_clear((PyObject *)self); } Py_RETURN_NONE; running: @@ -1881,23 +1985,24 @@ frame_clear(PyObject *op, PyObject *Py_UNUSED(ignored)) return NULL; } -PyDoc_STRVAR(clear__doc__, -"F.clear(): clear all references held by the frame"); +/*[clinic input] +@critical_section +frame.__sizeof__ + +Return the size of the frame in memory, in bytes. +[clinic start generated code]*/ static PyObject * -frame_sizeof(PyObject *op, PyObject *Py_UNUSED(ignored)) +frame___sizeof___impl(PyFrameObject *self) +/*[clinic end generated code: output=82948688e81078e2 input=908f90a83e73131d]*/ { - PyFrameObject *f = PyFrameObject_CAST(op); Py_ssize_t res; res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus); - PyCodeObject *code = _PyFrame_GetCode(f->f_frame); + PyCodeObject *code = _PyFrame_GetCode(self->f_frame); res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *); return PyLong_FromSsize_t(res); } -PyDoc_STRVAR(sizeof__doc__, -"F.__sizeof__() -> size of F in memory, in bytes"); - static PyObject * frame_repr(PyObject *op) { @@ -1910,8 +2015,8 @@ frame_repr(PyObject *op) } static PyMethodDef frame_methods[] = { - {"clear", frame_clear, METH_NOARGS, clear__doc__}, - {"__sizeof__", frame_sizeof, METH_NOARGS, sizeof__doc__}, + FRAME_CLEAR_METHODDEF + FRAME___SIZEOF___METHODDEF {NULL, NULL} /* sentinel */ }; @@ -2230,10 +2335,12 @@ PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { assert(frame != NULL); + PyObject *code; + Py_BEGIN_CRITICAL_SECTION(frame); assert(!_PyFrame_IsIncomplete(frame->f_frame)); - PyCodeObject *code = _PyFrame_GetCode(frame->f_frame); - assert(code != NULL); - return (PyCodeObject*)Py_NewRef(code); + code = Py_NewRef(_PyFrame_GetCode(frame->f_frame)); + Py_END_CRITICAL_SECTION(); + return (PyCodeObject *)code; } @@ -2257,41 +2364,38 @@ PyObject* PyFrame_GetLocals(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); - return frame_getlocals((PyObject *)frame, NULL); + return frame_locals_get((PyObject *)frame, NULL); } PyObject* PyFrame_GetGlobals(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); - return frame_getglobals((PyObject *)frame, NULL); + return frame_globals_get((PyObject *)frame, NULL); } PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); - return frame_getbuiltins((PyObject *)frame, NULL); + return frame_builtins_get((PyObject *)frame, NULL); } int PyFrame_GetLasti(PyFrameObject *frame) { + int ret; + Py_BEGIN_CRITICAL_SECTION(frame); assert(!_PyFrame_IsIncomplete(frame->f_frame)); int lasti = _PyInterpreterFrame_LASTI(frame->f_frame); - if (lasti < 0) { - return -1; - } - return lasti * sizeof(_Py_CODEUNIT); + ret = lasti < 0 ? -1 : lasti * (int)sizeof(_Py_CODEUNIT); + Py_END_CRITICAL_SECTION(); + return ret; } PyObject * PyFrame_GetGenerator(PyFrameObject *frame) { assert(!_PyFrame_IsIncomplete(frame->f_frame)); - if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) { - return NULL; - } - PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame->f_frame); - return Py_NewRef(gen); + return frame_generator_get((PyObject *)frame, NULL); } diff --git a/Python/frame.c b/Python/frame.c index 166ce07882126a..462202451f9f9d 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -49,6 +49,7 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame) static void take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) { + Py_BEGIN_CRITICAL_SECTION(f); assert(frame->owner < FRAME_OWNED_BY_INTERPRETER); assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); _PyInterpreterFrame *new_frame = (_PyInterpreterFrame *)f->_f_frame_data; @@ -85,6 +86,7 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { _PyObject_GC_TRACK((PyObject *)f); } + Py_END_CRITICAL_SECTION(); } void @@ -114,7 +116,7 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) if (frame->frame_obj) { PyFrameObject *f = frame->frame_obj; frame->frame_obj = NULL; - if (Py_REFCNT(f) > 1) { + if (!_PyObject_IsUniquelyReferenced((PyObject *)f)) { take_ownership(f, frame); Py_DECREF(f); return; _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com