Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appropriate error handling in initializing a data structure with explicit resource management?

Tags:

common-lisp

When initializing a data structure, or object, which has sub objects requiring explicit release process after used, how should I handle an error during the initialization process?

Let me take an example, initializing an OBJECT object with SUBOBJ1 and SUBOBJ2 slots to be set foreign pointers to int values:

(defun init-object ()
  (let ((obj (make-object)))
    (setf (subobj1 obj) (cffi:foreign-alloc :int)
          (subobj2 obj) (cffi:foreign-alloc :int))
    obj))

If we had an error in FOREIGN-ALLOCing for SUBOBJ2 slot, we should do FOREIGN-FREEing for SUBOBJ1 slot to avoid memory leaking.

As an idea, I can write as below:

(defun init-object ()
  (let ((obj (make-object)))
    (handler-case
        (setf (subobj1 obj) (cffi:foreign-alloc :int)
              (subobj2 obj) (cffi:foreign-alloc :int))
      (condition (c)   ; forcedly handling all conditions
        (when (subobj1 obj) (cffi:foreign-free (subobj1 obj)))
        (error c)))    ; invoke the condition c again explicitly
    obj))

Do you have any better idea, or generally idiomatic pattern?

Thanks


Following the answers, I add a code using UNWIND-PROTECT. It would not work because the deallocating forms run even if all allocation are completed successfully.

(defun init-object ()
  (let ((obj (make-object)))
    (unwind-protect
      (progn
        (setf (subobj1 obj) (cffi:foreign-alloc :int)
              (subobj2 obj) (cffi:foreign-alloc :int))
        obj)
      ; foreign pointers freed even when successfully initialized
      (when (subobj2 obj) (cffi:foreign-free (subobj2 obj)))
      (when (subobj1 obj) (cffi:foreign-free (subobj1 obj))))))
like image 674
masayuki takagi Avatar asked Nov 28 '12 12:11

masayuki takagi


1 Answers

Use UNWIND-PROTECT. When the error causes an exit out of the scope, unwind-protect allows you to force the execution of clean-up forms.

Something like this:

(defun init-object ()
  (let ((obj (make-object)))
    (unwind-protect
        (setf (subobj1 obj) (cffi:foreign-alloc :int)
              (subobj2 obj) (cffi:foreign-alloc :int))
      (unless (and (subobj2 obj) (subobj1 obj))
        (when (subobj1 obj) (cffi:foreign-free (subobj1 obj)))
        (when (subobj2 obj) (cffi:foreign-free (subobj2 obj)))))
    obj))

Use whatever is available to detect if a slot is bound or not. Above assumes that a non-initialized slot has a value of NIL.

like image 75
Rainer Joswig Avatar answered Sep 28 '22 07:09

Rainer Joswig