abcl icon indicating copy to clipboard operation
abcl copied to clipboard

The problem with multi-threading.

Open hyotang666 opened this issue 4 years ago • 0 comments

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.

hyotang666 avatar Oct 30 '21 09:10 hyotang666