From e28d8aef2db27e7d86656361b54f64edcbd84906 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 22 Mar 2001 16:30:17 +0000 Subject: 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. --- Doc/api/api.tex | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 4 deletions(-) diff --git a/Doc/api/api.tex b/Doc/api/api.tex index 5ff8fda..5726341 100644 --- a/Doc/api/api.tex +++ b/Doc/api/api.tex @@ -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. -- cgit v0.12