garbage-collectionlispcommon-lispfinalizerfinalization

How to finalize lisp:struct containing a pointer?


I am porting Lightweight Communications and Marshalling from julia to lisp as it has a better API. I used swig to generate C function calls.

I want to know if this is a safe usage for C pointer or not. here is the create function:

(defun create-lcm (&optional (provider (null-pointer)))
    (let* ((ptr (lcm_create provider))
           (addr (cffi:pointer-address ptr)))
        (tg:finalize ptr (lambda () (lcm_destroy (cffi:make-pointer addr))))
        (if (NULL-POINTER-P ptr)
                (error "lcm creation error"))
        (%create-lcm :pointer ptr :provider provider
                                 :file-descriptor (lcm_get_fileno ptr))))

Question:

Any other notes/advices are welcome.

Thanks in advance.


Solution

  • Here are a few things that were wrong:

    1. Attaching a finaliser to a pointer that might be null
    2. I’m not sure you’re allowed to attach a finaliser to a foreign pointer. Maybe you are.
    3. You need to be careful with finalisers and gc. If the finaliser references the object that it finalises then the object and its finaliser keep each other alive (they can’t be collected at once because a finaliser might store a reference to the object somewhere and then that object would be alive and so shouldn’t have been finalised.

    I don’t know if this is right but it is better:

    (defun create-lcm (&optional (provider (null-pointer))
      (let ((ptr (lcm_create provider)))
        (when (null-pointer-p ptr)
          (error “lcm creation error”))
        (flet ((finaliser () (lcm_destroy ptr)))
          (let ((result (%create-lcm :pointer ptr :provider provider
                                     :file-descriptor (lcm_get_fileno ptr))))
            (tg:finalize result #'finaliser)
            result))))
    

    Here are some things that are wrong:

    1. If there is an error from %create-lcm or lcm_get_fileno then the finaliser will not run