gh-138794: Communicate to PyRefTracer when they are being replaced (#138797)
This commit is contained in:
committed by
GitHub
parent
baf7470515
commit
f01181b595
@@ -2010,6 +2010,11 @@ Reference tracing
|
||||
is set to :c:data:`PyRefTracer_DESTROY`). The **data** argument is the opaque pointer
|
||||
that was provided when :c:func:`PyRefTracer_SetTracer` was called.
|
||||
|
||||
If a new tracing function is registered replacing the current a call to the
|
||||
trace function will be made with the object set to **NULL** and **event** set to
|
||||
:c:data:`PyRefTracer_TRACKER_REMOVED`. This will happen just before the new
|
||||
function is registered.
|
||||
|
||||
.. versionadded:: 3.13
|
||||
|
||||
.. c:var:: int PyRefTracer_CREATE
|
||||
@@ -2022,6 +2027,13 @@ Reference tracing
|
||||
The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python
|
||||
object has been destroyed.
|
||||
|
||||
.. c:var:: int PyRefTracer_TRACKER_REMOVED
|
||||
|
||||
The value for the *event* parameter to :c:type:`PyRefTracer` functions when the
|
||||
current tracer is about to be replaced by a new one.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: int PyRefTracer_SetTracer(PyRefTracer tracer, void *data)
|
||||
|
||||
Register a reference tracer function. The function will be called when a new
|
||||
@@ -2037,6 +2049,10 @@ Reference tracing
|
||||
|
||||
There must be an :term:`attached thread state` when calling this function.
|
||||
|
||||
If another tracer function was already registered, the old function will be
|
||||
called with **event** set to :c:data:`PyRefTracer_TRACKER_REMOVED` just before
|
||||
the new function is registered.
|
||||
|
||||
.. versionadded:: 3.13
|
||||
|
||||
.. c:function:: PyRefTracer PyRefTracer_GetTracer(void** data)
|
||||
|
||||
@@ -463,6 +463,7 @@ PyAPI_FUNC(int) PyUnstable_Type_AssignVersionTag(PyTypeObject *type);
|
||||
typedef enum {
|
||||
PyRefTracer_CREATE = 0,
|
||||
PyRefTracer_DESTROY = 1,
|
||||
PyRefTracer_TRACKER_REMOVED = 2,
|
||||
} PyRefTracerEvent;
|
||||
|
||||
typedef int (*PyRefTracer)(PyObject *, PyRefTracerEvent event, void *);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
When a new tracing function is registered with
|
||||
:c:func:`PyRefTracer_SetTracer`, replacing the current a call to the trace
|
||||
function will be made with the object set to **NULL** and **event** set to
|
||||
:c:data:`PyRefTracer_TRACKER_REMOVED`. This will happen just before the new
|
||||
function is registered. Patch by Pablo Galindo
|
||||
@@ -2319,6 +2319,7 @@ test_weakref_capi(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
|
||||
struct simpletracer_data {
|
||||
int create_count;
|
||||
int destroy_count;
|
||||
int tracker_removed;
|
||||
void* addresses[10];
|
||||
};
|
||||
|
||||
@@ -2326,10 +2327,18 @@ static int _simpletracer(PyObject *obj, PyRefTracerEvent event, void* data) {
|
||||
struct simpletracer_data* the_data = (struct simpletracer_data*)data;
|
||||
assert(the_data->create_count + the_data->destroy_count < (int)Py_ARRAY_LENGTH(the_data->addresses));
|
||||
the_data->addresses[the_data->create_count + the_data->destroy_count] = obj;
|
||||
if (event == PyRefTracer_CREATE) {
|
||||
the_data->create_count++;
|
||||
} else {
|
||||
the_data->destroy_count++;
|
||||
switch (event) {
|
||||
case PyRefTracer_CREATE:
|
||||
the_data->create_count++;
|
||||
break;
|
||||
case PyRefTracer_DESTROY:
|
||||
the_data->destroy_count++;
|
||||
break;
|
||||
case PyRefTracer_TRACKER_REMOVED:
|
||||
the_data->tracker_removed++;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2393,6 +2402,10 @@ test_reftracer(PyObject *ob, PyObject *Py_UNUSED(ignored))
|
||||
PyErr_SetString(PyExc_ValueError, "The object destruction was not correctly traced");
|
||||
goto failed;
|
||||
}
|
||||
if (tracer_data.tracker_removed != 1) {
|
||||
PyErr_SetString(PyExc_ValueError, "The tracker removal was not correctly traced");
|
||||
goto failed;
|
||||
}
|
||||
PyRefTracer_SetTracer(current_tracer, current_data);
|
||||
Py_RETURN_NONE;
|
||||
failed:
|
||||
@@ -2533,11 +2546,15 @@ code_offset_to_line(PyObject* self, PyObject* const* args, Py_ssize_t nargsf)
|
||||
static int
|
||||
_reftrace_printer(PyObject *obj, PyRefTracerEvent event, void *counter_data)
|
||||
{
|
||||
if (event == PyRefTracer_CREATE) {
|
||||
printf("CREATE %s\n", Py_TYPE(obj)->tp_name);
|
||||
}
|
||||
else { // PyRefTracer_DESTROY
|
||||
printf("DESTROY %s\n", Py_TYPE(obj)->tp_name);
|
||||
switch (event) {
|
||||
case PyRefTracer_CREATE:
|
||||
printf("CREATE %s\n", Py_TYPE(obj)->tp_name);
|
||||
break;
|
||||
case PyRefTracer_DESTROY:
|
||||
printf("DESTROY %s\n", Py_TYPE(obj)->tp_name);
|
||||
break;
|
||||
case PyRefTracer_TRACKER_REMOVED:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3287,6 +3287,12 @@ _Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt)
|
||||
|
||||
int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) {
|
||||
_Py_AssertHoldsTstate();
|
||||
if (_PyRuntime.ref_tracer.tracer_func != NULL) {
|
||||
_PyReftracerTrack(NULL, PyRefTracer_TRACKER_REMOVED);
|
||||
if (PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
_PyRuntime.ref_tracer.tracer_func = tracer;
|
||||
_PyRuntime.ref_tracer.tracer_data = data;
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user