Как завершить lisp:struct, содержащую указатель?

Я портирую Lightweight Communications и Marshalling с julia на lisp, так как он имеет лучший API. Я использовал Swig для генерации вызовов функции C.

Я хочу знать, является ли это безопасным использованием указателя C или нет. вот функция создания:

(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))))

Вопрос:

  • Как правильно завершить указатель C?
  • Как сделать тест для этого?

Любые другие заметки / советы приветствуются.

Заранее спасибо.

2 ответа

Решение

Вот несколько вещей, которые были не правы:

  1. Присоединение финализатора к указателю, который может быть нулевым
  2. Я не уверен, что вам разрешено прикреплять финализатор к внешнему указателю. Может быть ты.
  3. Вы должны быть осторожны с финализаторами и gc. Если финализатор ссылается на объект, который он завершает, то этот объект и его финализатор поддерживают друг друга живыми (их нельзя собрать сразу, поскольку финализатор может где-то хранить ссылку на объект, и тогда этот объект будет жив и поэтому не должен '). т были завершены.

Я не знаю, правильно ли это, но лучше

(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))))

Вот некоторые вещи, которые не так:

  1. Если есть ошибка от %create-lcm или же lcm_get_fileno тогда финализатор не запустится

Вы можете прочитать о cl-autowrap, который используется, прежде всего, для упаковки SDL 2 в cl-sdl2, Библиотека предоставляет тонкие обертки вокруг указателей, которые уже освобождают память при завершении.

Я также думаю, что рекомендуемый способ использования финализаторов - это использовать их только для устранения возможных утечек, учитывая, что у вас мало контроля над тем, когда и как выполняется функция очистки (например, какой поток, какая динамическая среда).

Один из способов управления памятью - это заранее выделить свои структуры и очистить их, когда они вам больше не нужны (пул). Или вы можете определить функцию или макрос, который определяет область видимости, такую, что память выделяется при входе в нее и освобождается при выходе, используя unwind-protect:

(defmacro with-lcm ((context &rest options) &body body)
  (let ((internal (gensym))) 
    `(let* ((,internal (create-lcm ,@options))
            (,context ,internal))
       (unwind-protect (progn ,@body)
         (destroy-lcm ,internal)))))
Другие вопросы по тегам