The problem with multi-threading.
What I got is the behavior like below.
CL-USER(0): (asdf:load-system :bordeaux-threads)
T
CL-USER(1): (bt:with-timeout (1) (error "test"))
#<THREAD "interpreter" {7C1F6E9E}>: Debugger invoked on condition of type SIMPLE-ERROR
test
Restarts:
0: TOP-LEVEL Return to top level.
[1] CL-USER(2): 0
CL-USER(3): (asdf:load-system :bordeaux-threads)
#<THREAD "interpreter" {7C1F6E9E}>: Debugger invoked on condition of type STREAM-ERROR
java.nio.channels.ClosedByInterruptException
Restarts:
0: RETRY Retry ASDF operation.
1: CLEAR-CONFIGURATION-AND-RETRY Retry ASDF operation after resetting the configuration.
2: TOP-LEVEL Return to top level.
[1] CL-USER(4): 2
CL-USER(5):
Very interestingly, cl:sleep resolve the problem.
CL-USER(5): (sleep 1)
NIL
CL-USER(6): (asdf:load-system :bordeaux-threads)
T
This is the code that I used to chase the point.
This code is made by expanding the bt:with-timeout then replacing all of bt symbols to abcl threads symbols and replacing the alexandria:unwind-protect-case to cl:unwind-protect and adding traditional printf debugging codes.
(define-condition interrupt ()
((tag :initarg :tag :reader interrupt-tag)))
(let (interrupt-thread)
(unwind-protect
(let* ((interrupt-tag (gensym "INTERRUPT-TAG-"))
(caller (threads:current-thread)))
(setf interrupt-thread
(threads:make-thread
(lambda ()
(sleep 10)
(print :slept) (force-output)
(threads:interrupt-thread
caller
(lambda () (print :comes) (signal 'interrupt :tag interrupt-tag))))
:name (format nil "WITH-TIMEOUT thread serving: ~S."
(threads:thread-name caller))))
(handler-bind
((interrupt (lambda (c)
(when (eql interrupt-tag (interrupt-tag c))
(error "timeout")))))
(error "test")))
(print :cleanup)
(when (and (print interrupt-thread) (print (threads:thread-alive-p interrupt-thread)))
(ignore-errors (progn (when (eq interrupt-thread (threads:current-thread))
(print :can-not)
(error "Cannot destroy the current thread"))
(print :destroy)
(threads:destroy-thread interrupt-thread))))))
If the body form successfully returns in timeout time, everything works fine.
:CLEANUP ; <--- Into the unwind-protect cleanup clause.
#<THREAD "WITH-TIMEOUT thread serving: "interpreter"." {5D002021}>
T ; <--- Thread alive.
:DESTROY ; <--- Successfully reach to the command destroy-thread.
CL-USER(6): (asdf:load-system :bordeaux-threads)
T
If the body form works over timeout, everything works as expected.
:SLEPT ; <--- Thread function reach to the threads:interrupt-thread.
:COMES ; <--- An anonymous function which the threads:inperrupt-thread accept is called.
#<THREAD "interpreter" {7C1F6E9E}>: Debugger invoked on condition of type SIMPLE-ERROR
timeout
Restarts:
0: TOP-LEVEL Return to top level.
[1] CL-USER(n): 0
:CLEANUP ; <--- Into the unwind-protect cleanup clause.
#<THREAD "WITH-TIMEOUT thread serving: "interpreter"." {2C0F6591}>
NIL ; <--- Thread is not alive.
CL-USER(6): (asdf:load-system :bordeaux-threads)
T
If body form invokes debugger and choosing restart in timeout time, everything works fine.
:CLEANUP ; <--- Into the unwind-protect cleanup clause.
#<THREAD "WITH-TIMEOUT thread serving: "interpreter"." {5D002021}>
T ; <--- Thread alive.
:DESTROY ; <--- Successfully reach to the command destroy-thread.
CL-USER(6): (asdf:load-system :bordeaux-threads)
T
If body form invokes debugger and choosing restart after timeout time, fall into the issue.
#<THREAD "interpreter" {7C1F6E9E}>: Debugger invoked on condition of type SIMPLE-ERROR
test
Restarts:
0: TOP-LEVEL Return to top level.
[1] CL-USER(2):
:SLEPT ; <--- Thread function reach to the command interrupt-thread.
; It seems that the anonymous function that thread:interrupt-thread accepted is not called.
0 ; <--- Choosing the restart.
:CLEANUP ; <--- Into the unwind-protect cleanup clause.
#<THREAD "WITH-TIMEOUT thread serving: "interpreter"." {289685CC}>
NIL ; <--- Thread is not alive.
CL-USER(3): (asdf:load-system :bordeaux-threads)
#<THREAD "interpreter" {7C1F6E9E}>: Debugger invoked on condition of type STREAM-ERROR
java.nio.channels.ClosedByInterruptException
Restarts:
0: RETRY Retry ASDF operation.
1: CLEAR-CONFIGURATION-AND-RETRY Retry ASDF operation after resetting the configuration.
2: TOP-LEVEL Return to top level.
[1] CL-USER(4): 0
CL-USER(5): (sleep 0)
:COMES ; <--- Anonymous function which the threads:inperrupt-thread accept is called.
NIL
CL-USER(6): (asdf:load-system :bordeaux-threads)
T
This behavior may be related to #134.