Hi Jim, Have you added the corresponding code in request_tp_clear and request_tp_traverse ?
I think you can check-in your code, since the debate about import looks like it is going to take som time before the next release... Regards, Nicolas 2005/6/12, Jim Gallacher <[EMAIL PROTECTED]>: > Nicolas Lehuen wrote: > > Hi Jim, > > > > After a few checks (unittest + load testing), I've checked in my > > modifications ; you might want to update and merge it with your code. > > I'm still getting a memory leak with the merged code. Should I commit my > changes anyway, or maybe we could create new svn branch for this testing? > > Jim > > > Regards, > > Nicolas > > > > 2005/6/12, Jim Gallacher <[EMAIL PROTECTED]>: > > > >>Nicolas Lehuen wrote: > >> > >>>You should cast self to a requestobject* : > >> > >>That worked. Running some tests. > >> > >>Jim > >> > >> > >>>((requestobject*)self)->session > >>> > >>>Anyway, I'm currently rewriting the whole request_tp_traverse / > >>>request_tp_clear / request_tp_clear functions like this : > >>> > >>>static void request_tp_dealloc(requestobject *self) > >>>{ > >>> // de-register the object from the GC > >>> // before its deallocation, to prevent the > >>> // GC to run on a partially de-allocated object > >>> PyObject_GC_UnTrack(self); > >>> > >>> request_tp_clear(self); > >>> > >>> PyObject_GC_Del(self); > >>>} > >>> > >>>/** > >>> ** request_tp_traverse > >>> ** > >>> * Traversal of the request object > >>> */ > >>>static int request_tp_traverse(requestobject* self, visitproc visit, > >>>void *arg) { > >>> int result; > >>> > >>> if(self->dict) {result = visit(self->dict,arg); if(result) return > >>> result;} > >>> if(self->connection) {result = visit(self->connection,arg); > >>>if(result) return result;} > >>> if(self->server) {result = visit(self->server,arg); if(result) > >>>return result;} > >>> if(self->next) {result = visit(self->next,arg); if(result) return > >>> result;} > >>> if(self->prev) {result = visit(self->prev,arg); if(result) return > >>> result;} > >>> if(self->main) {result = visit(self->main,arg); if(result) return > >>> result;} > >>> if(self->headers_in) {result = visit(self->headers_in,arg); > >>>if(result) return result;} > >>> if(self->headers_out) {result = visit(self->headers_out,arg); > >>>if(result) return result;} > >>> if(self->err_headers_out) {result = > >>>visit(self->err_headers_out,arg); if(result) return result;} > >>> if(self->subprocess_env) {result = > >>>visit(self->subprocess_env,arg); if(result) return result;} > >>> if(self->notes) {result = visit(self->notes,arg); if(result) return > >>> result;} > >>> if(self->phase) {result = visit(self->phase,arg); if(result) return > >>> result;} > >>> > >>> // no need to Py_DECREF(dict) since the reference is borrowed > >>> return 0; > >>>} > >>> > >>>static int request_tp_clear(requestobject *self) > >>>{ > >>> PyObject* tmp; > >>> tmp=self->dict; self->dict=NULL; Py_XDECREF(tmp); > >>> tmp=self->connection; self->connection=NULL; Py_XDECREF(tmp); > >>> tmp=self->server; self->server=NULL; Py_XDECREF(tmp); > >>> tmp=self->next; self->next=NULL; Py_XDECREF(tmp); > >>> tmp=self->prev; self->prev=NULL; Py_XDECREF(tmp); > >>> tmp=self->main; self->main=NULL; Py_XDECREF(tmp); > >>> tmp=self->headers_in; self->headers_in=NULL; Py_XDECREF(tmp); > >>> tmp=self->headers_out; self->headers_out=NULL; Py_XDECREF(tmp); > >>> tmp=self->err_headers_out; self->err_headers_out=NULL; Py_XDECREF(tmp); > >>> tmp=self->subprocess_env; self->subprocess_env=NULL; Py_XDECREF(tmp); > >>> tmp=self->notes; self->notes=NULL; Py_XDECREF(tmp); > >>> tmp=self->phase; self->phase=NULL; Py_XDECREF(tmp); > >>> tmp=self->hlo; self->hlo=NULL; Py_XDECREF(tmp); > >>> tmp=self->dict; self->dict=NULL; Py_XDECREF(tmp); > >>> tmp=self->dict; self->dict=NULL; Py_XDECREF(tmp); > >>> tmp=self->dict; self->dict=NULL; Py_XDECREF(tmp); > >>> tmp=self->dict; self->dict=NULL; Py_XDECREF(tmp); > >>> > >>> return 0; > >>>} > >>> > >>>As you can guess from the source code, we'll have to add some code for > >>>the session member when you'll integrate your code in subversion. > >>> > >>>Regards, > >>>Nicolas > >>> > >>>2005/6/12, Jim Gallacher <[EMAIL PROTECTED]>: > >>> > >>> > >>>>Nicolas, > >>>> > >>>>It fails to compile when I add your bit of code. Sure looks like it > >>>>should work though. > >>>> > >>>>requestobject.c: In function `request_tp_traverse': > >>>>requestobject.c:1539: error: structure has no member named `session' > >>>>requestobject.c:1540: error: structure has no member named `session' > >>>> > >>>>Just to be clear, it compiles and runs just fine without the extra check > >>>> added into request_tp_traverse. > >>>> > >>>>Nicolas Lehuen wrote: > >>>> > >>>> > >>>>>I'm re-reading the comment I wrote when I implemented tp_traverse : > >>>>> > >>>>>// 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) > >>>>> > >>>>>Turns out that we should at least traverse the next and prev in case > >>>>>someone does something like req.next.my_prev = req. That's what I > >>>>>meant by "unless you really want it". > >>>> > >>>>I wondered about that. :) > >>>> > >>>>Jim > >>>> > >>>> > >>>> > >>>>>It's pretty difficult to get > >>>>>there (you need an internal redirect), but you can. So I'm going to > >>>>>have a look at re-implementing the tp_traverse handler. > >>>>>Regards, > >>>>>Nicolas > >>>>> > >>>>>2005/6/12, Nicolas Lehuen <[EMAIL PROTECTED]>: > >>>>> > >>>>> > >>>>> > >>>>>>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) > >>>>>>> > >>>>>> > >> > > > >