[cffi-devel] A question about memory management and garbage collection

Martin Simmons martin at lispworks.com
Mon May 9 16:44:58 UTC 2011


>>>>> On Mon, 09 May 2011 16:46:20 +0200, nitralime  said:
> 
> On 05/09/2011 01:49 PM, Martin Simmons wrote:
> >>>>>> On Sun, 08 May 2011 14:39:41 +0200, Nitralime  said:
> >> Using a finalizer seems to be a possible way to go. But I'm not sure how
> >> this can be done by just using the finalizer parameters "object" and
> >> "function"
> >> where "function" can't reliablely access "object" (cf. documentation of
> >> finalizer
> >> in "trivial-garbage"  package)!!
> >>
> >> Any help and feedback is very much appreciated!
> > IMHO, attempting to transparently wrap a foreign object with a CLOS object is
> > not a good design.
> >
> > Using a finalizer is dangerous because it is difficult to guarantee that the
> > Lisp wrapper object is retained for the correct lifetime.
> >
> > For example, consider
> >
> > (defstruct table
> >    foreign-pointer)
> >
> > (defun allocate-table ()
> >    (make-table :foreign-pointer (foreign-allocate-table)))
> >
> > (defun allocate-and-munge-table ()
> >    (let ((table (allocate-table)))
> >      (foreign-munge-table (table-foreign-pointer table))))
> >
> > The compiler might not keep the variable "table" alive during the call to the
> > foreign function foreign-munge-table.  As a result, the Lisp table object
> > might be gc'ed in another thread while the foreign code is accessing it.
> >
> Consider the following sample (pseudo-)code:
> ----------------------------------------------------------
> (defclass table ()
> ((table-pointer :initarg :handle :accessor handle-of)))
> 
> (defmethod initialize-instance :after ((self table) &key)
> (let ((table-pointer (handle-of self)))
> (tg:finalize self (lambda () (foreign-destroy-table table-pointer)))))
> 
> (defmethod get-row ((t table) index)
> (transform-to-lisp-vector (foreign-get-row (hadle-of t) index)))
> 
> (defmethod move-to ((t table) index)
> (foreign-move-to (handle-of t) index))
> .....
> ----------------------------------------------------------
> 
> Now consider a function which creates a table instance
> and puts it into a result plist (and has probabely some side effects):
> 
> (defun call-a-foreign-function (.....)
> (let (....)
> ....
> <call a foreign function which allocates a table object
> and returns a-foreign-table-pointer>
> ....
> (let ((result-table (make-instance 'table :handle a-foreign-table-pointer))
> (...))
> ...
> ...
> '(:id ...
> :tab result-table
> :msg ...
> ...))))
> 
> The question is now what will happen if you do just
> 
> (call-a-foreign-function ...)
> 
> or something like this
> 
> (getf (call-a-foreign-function ...) :id)
> 
> I assume that the unreachable object 'result-table' will be garbage 
> collected,
> and the finalization will free the corresponding C table object!
> 
> Do you see any misconception (resp. potential danger) here?

Your examples will be safe, but

(get-row (getf (call-a-foreign-function ...) :tab) 0)

might crash.

-- 
Martin Simmons
LispWorks Ltd
http://www.lispworks.com/




More information about the cffi-devel mailing list