abcl icon indicating copy to clipboard operation
abcl copied to clipboard

UNWIND-PROTECT cleanup forms are not evaluated on thread destruction

Open sionescu opened this issue 4 years ago • 2 comments

It looks like THREADS:DESTROY-THREAD does not evaluate cleanup forms, but simply terminates the thread.

Test case:

(defparameter *test* nil)

(defun thread-termination-test ()
  (setf *test* nil)
  (flet ((thread-fn ()
           (setf *test* :entered)
           (unwind-protect
               (progn
                 ;; Ensure that the thread is blocked here
                 ;; when DESTROY-THREAD is called.
                 (sleep 3)
                 (setf *test* :failed))
             (when (eq *test* :entered)
               (setf *test* :success)))))
    (let ((thread (threads:make-thread #'thread-fn)))
      (sleep 1)
      (threads:destroy-thread thread)
      (threads:thread-join thread)
      *test*)))

On all Lisps that I can get my hands on, the equivalent code in Bordeaux-Threads returns :SUCCESS, while ABCL returns :ENTERED.

sionescu avatar Jan 08 '22 23:01 sionescu

Weird.

https://github.com/armedbear/abcl/blob/36a4b5994227d768882ff6458b3df9f79caac664/src/org/armedbear/lisp/Lisp.java#L515-L516

If a thread is marked to be destroyed, then a Java error (not an exception) is eventually thrown. That should unwind the stack and therefore should execute unwind-protect cleanup forms.

Are these defined to behave like Java finally? Unwinding the Java stack should cause the finally forms between the Java throw and the Java catch to be executed.

phoe avatar Jan 09 '22 07:01 phoe

A solution that should always work, but that would change the architecture of destroying threads, would be to implement destroying a thread via interrupting it with (lambda () (throw '%abcl-destroy-thread nil)) after wrapping all thread code in (catch '%abcl-destroy-thread ...) - if CL throws and catches work fine, then this should also work fine.

phoe avatar Jan 09 '22 07:01 phoe