Be more clear about the specific rules for supporting the cyclic GC in an

extension object.  Also included an example showing exactly what needs to
be done and nothing else.

This closes SF bug #228591.
This commit is contained in:
Fred Drake 2001-03-22 16:30:17 +00:00
parent f5db48e72e
commit e28d8aef2d
1 changed files with 126 additions and 4 deletions

View File

@ -4975,10 +4975,10 @@ to provide any explicit support for garbage collection.
To create a container type, the \member{tp_flags} field of the type
object must include the \constant{Py_TPFLAGS_GC} and provide an
implementation of the \member{tp_traverse} handler. The value of the
\member{tp_basicsize} field must include \constant{PyGC_HEAD_SIZE} as
well. If instances of the type are mutable, a \member{tp_clear}
implementation must also be provided.
implementation of the \member{tp_traverse} handler. The computed
value of the \member{tp_basicsize} field must include
\constant{PyGC_HEAD_SIZE} as well. If instances of the type are
mutable, a \member{tp_clear} implementation must also be provided.
\begin{datadesc}{Py_TPFLAGS_GC}
Objects with a type with this flag set must conform with the rules
@ -4992,6 +4992,17 @@ implementation must also be provided.
collector is disabled at compile time then this is \code{0}.
\end{datadesc}
Constructors for container types must conform to two rules:
\begin{enumerate}
\item The memory for the object must be allocated using
\cfunction{PyObject_New()} or \cfunction{PyObject_VarNew()}.
\item Once all the fields which may contain references to other
containers are initialized, it must call
\cfunction{PyObject_GC_Init()}.
\end{enumerate}
\begin{cfuncdesc}{void}{PyObject_GC_Init}{PyObject *op}
Adds the object \var{op} to the set of container objects tracked by
the collector. The collector can run at unexpected times so objects
@ -5000,6 +5011,17 @@ implementation must also be provided.
usually near the end of the constructor.
\end{cfuncdesc}
Similarly, the deallocator for the object must conform to a similar
pair of rules:
\begin{enumerate}
\item Before fields which refer to other containers are invalidated,
\cfunction{PyObject_GC_Fini()} must be called.
\item The object's memory must be deallocated using
\cfunction{PyObject_Del()}.
\end{enumerate}
\begin{cfuncdesc}{void}{PyObject_GC_Fini}{PyObject *op}
Remove the object \var{op} from the set of container objects tracked
by the collector. Note that \cfunction{PyObject_GC_Init()} can be
@ -5045,6 +5067,106 @@ The \member{tp_clear} handler must be of the \ctype{inquiry} type, or
\end{ctypedesc}
\subsection{Example Cycle Collector Support
\label{example-cycle-support}}
This example shows only enough of the implementation of an extension
type to show how the garbage collector support needs to be added. It
shows the definition of the object structure, the
\member{tp_traverse}, \member{tp_clear} and \member{tp_dealloc}
implementations, the type structure, and a constructor --- the module
initialization needed to export the constructor to Python is not shown
as there are no special considerations there for the collector. To
make this interesting, assume that the module exposes ways for the
\member{container} field of the object to be modified. Note that
since no checks are made on the type of the object used to initialize
\member{container}, we have to assume that it may be a container.
\begin{verbatim}
#include "Python.h"
typedef struct {
PyObject_HEAD
PyObject *container;
} MyObject;
static int
my_traverse(MyObject *self, visitproc visit, void *arg)
{
if (self->container != NULL)
return visit(self->container, arg);
else
return 0;
}
static int
my_clear(MyObject *self)
{
Py_XDECREF(self->container);
self->container = NULL;
return 0;
}
static void
my_dealloc(MyObject *self)
{
PyObject_GC_Fini((PyObject *) self);
Py_XDECREF(self->container);
PyObject_Del(self);
}
\end{verbatim}
\begin{verbatim}
statichere PyTypeObject
MyObject_Type = {
PyObject_HEAD_INIT(NULL)
0,
"MyObject",
sizeof(MyObject) + PyGC_HEAD_SIZE,
0,
(destructor)my_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC,
0, /* tp_doc */
(traverseproc)my_traverse, /* tp_traverse */
(inquiry)my_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
};
/* This constructor should be made accessible from Python. */
static PyObject *
new_object(PyObject *unused, PyObject *args)
{
PyObject *container = NULL;
MyObject *result = NULL;
if (PyArg_ParseTuple(args, "|O:new_object", &container)) {
result = PyObject_New(MyObject, &MyObject_Type);
if (result != NULL) {
result->container = container;
PyObject_GC_Init();
}
}
return (PyObject *) result;
}
\end{verbatim}
% \chapter{Debugging \label{debugging}}
%
% XXX Explain Py_DEBUG, Py_TRACE_REFS, Py_REF_DEBUG.