Duh, I get it. If you add a member to the request object, and this member is not referenced in the request object's dictionary, then you have to add a special case for it in the tp_traverse handler. In requestobject.c :
/** ** request_tp_traverse ** * Traversal of the request object */ static int request_tp_traverse(PyObject *self, visitproc visit, void *arg) { PyObject *dict,*values,*item,*str; int i,size; // only traverse its dictionary since other fields defined in request_rec_mbrs with type T_OBJECT // cannot be the source of memory leaks (unless you really want it) dict=*_PyObject_GetDictPtr(self); if(dict) { // this check is not needed, I guess, _PyObject_GetDictPtr always give a pointer to a dict object. if(PyDict_Check(dict)) { i = visit(dict,arg); if(i) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ((requestobject*)self)->request_rec, "%s:%i Call to visit() failed",__LINE__,__FILE__); // no need to Py_DECREF(dict) since the reference is borrowed return i; } } else { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ((requestobject*)self)->request_rec, "%s:%i Expected a dictionary",__LINE__,__FILE__); } } else { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ((requestobject*)self)->request_rec, "%s:%i Expected a dictionary",__LINE__,__FILE__); } /* This is the new code that needs to be added to support the new session member */ if(self->session) { i = visit(self->session,arg); if(i) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ((requestobject*)self)->request_rec, "%s:%i Call to visit() failed",__LINE__,__FILE__); return i; } } // no need to Py_DECREF(dict) since the reference is borrowed return 0; } Regards, Nicolas 2005/6/12, Jim Gallacher <[EMAIL PROTECTED]>: > Hi Nicolas, > > Nicolas Lehuen wrote: > > Hi Jim, > > > > How where you creating the session object in requestobject.c ? If you > > were using PythonObject_New, then this may explain the memory leak. > > Objects that must be managed by the garbage collector have to be > > created with PyObject_GC_New. > > The c-code loads the python module and calls a function which generates > the session object. > > src/requestobject.c > static PyObject *req_get_session(requestobject *self, PyObject *args) > { > PyObject *m; // session module > PyObject *sid; // session id > PyObject *result; > > if (!self->session) { > sid = PyObject_CallMethod(self->subprocess_env, "get", > "(ss)","REDIRECT_PYSID", ""); > > /******** > * This is where the session instance is created > ********/ > m = PyImport_ImportModule("mod_python.session2.TestSession"); > self->session = PyObject_CallMethod(m, "create_session", "(OO)", > self, sid); > > Py_DECREF(m); > Py_DECREF(sid); > if (self->session == NULL) > return NULL; > } > result = self->session; > Py_INCREF(result); > return result; > } > > ---------------------------------------------------------------- > mod_python/session2/TestSession.py > # emulate a simple test session > import _apache > from mod_python.Session import _new_sid > > class TestSession(object): > > def __init__(self, req, sid=0, secret=None, timeout=0, lock=1): > req.log_error("TestSession.__init__") > # Circular Reference causes problem > self._req = req > > # keeping a reference to the server object does > # NOT cause a problem > self._server = req.server > self._lock = 1 > self._locked = 0 > self._sid = _new_sid(req) > self.lock() > self.unlock() > > def lock(self): > if self._lock: > _apache._global_lock(self._req.server, self._sid) > self._locked = 1 > > def unlock(self): > # unlock will ocassionally segfault > if self._lock and self._locked: > _apache._global_unlock(self._req.server, self._sid) > self._locked = 0 > > def create_session(req,sid): > return TestSession(req,sid) >