* bug fix: remove a race condition in the setting of
funcallable-instance functions, this should make threaded CLOS
code more stable against memory faults.
+ * bug fix: corruption of specials when unbinding is interrupted by an
+ asynchronous unwind (reported by Hannu Koivisto)
* improvement: the debugger will now also display local variables that
are only used once, for code compiled with a DEBUG optimization quality
of 2 or higher.
Furthermore, @code{BIND} must always write the value to the binding
stack first and the symbol second because the symbol being non-zero
-means validity to @code{UNBIND-TO-HERE}.
+means validity to @code{UNBIND-TO-HERE}. For similar reasons
+@code{UNBIND} also zeroes the symbol first. But if it is interrupted
+by a signal that does an async unwind then @code{UNBIND-TO-HERE} can
+be triggered when the symbol is zeroed but the value is not. In this
+case @code{UNBIND-TO-HERE} must zero out the value to avoid leaving
+garbage around that may wreck the ship on the next @code{BIND}.
+
+In other words, the invariant is that the binding stack above bsp only
+contains zeros. This makes @code{BIND} safe in face of gc triggered at
+any point during its execution.
(loadw value bsp-tn (- binding-value-slot binding-size))
(#!+gengc storew-and-remember-slot #!-gengc storew
value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst subq bsp-tn (* 2 n-word-bytes) bsp-tn)))
(inst beq symbol skip)
(#!+gengc storew-and-remember-slot #!-gengc storew
value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
(emit-label skip)
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst subq bsp-tn (* 2 n-word-bytes) bsp-tn)
(inst cmpeq where bsp-tn temp)
(inst beq temp loop)
(loadw symbol bsp-tn (- binding-symbol-slot binding-size))
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst addi (- (* binding-size n-word-bytes)) bsp-tn bsp-tn)))
(define-vop (unbind-to-here)
(inst comb := symbol zero-tn skip)
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
SKIP
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst addi (* -2 n-word-bytes) bsp-tn bsp-tn)
(inst comb :<> where bsp-tn loop :nullify t)
(loadw symbol bsp-tn (- binding-symbol-slot binding-size))
(loadw symbol bsp-tn (- binding-symbol-slot binding-size))
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst addu bsp-tn bsp-tn (* -2 n-word-bytes))))
(inst beq symbol zero-tn skip)
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
(emit-label skip)
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst addu bsp-tn bsp-tn (* -2 n-word-bytes))
(inst bne where bsp-tn loop)
(inst nop)
(loadw symbol bsp-tn (- binding-symbol-slot binding-size))
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst subi bsp-tn bsp-tn (* 2 n-word-bytes))))
(inst beq skip)
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
(emit-label skip)
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst subi bsp-tn bsp-tn (* 2 n-word-bytes))
(inst cmpw where bsp-tn)
(inst bne loop)
(loadw symbol bsp-tn (- binding-symbol-slot binding-size))
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst sub bsp-tn bsp-tn (* 2 n-word-bytes))))
(define-vop (unbind-to-here)
(inst b :eq skip)
(loadw value bsp-tn (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(storew zero-tn bsp-tn (- binding-symbol-slot binding-size))
(emit-label skip)
+ (storew zero-tn bsp-tn (- binding-value-slot binding-size))
(inst sub bsp-tn bsp-tn (* 2 n-word-bytes))
(inst cmp where bsp-tn)
(inst b :ne loop)
(inst mov (make-ea :qword :base thread-base-tn :scale 1 :index tls-index)
value)
- (storew 0 bsp (- binding-value-slot binding-size))
(storew 0 bsp (- binding-symbol-slot binding-size))
+ (storew 0 bsp (- binding-value-slot binding-size))
(inst sub bsp (* binding-size n-word-bytes))
(store-binding-stack-pointer bsp)))
(loadw symbol bsp (- binding-symbol-slot binding-size))
(loadw value bsp (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew 0 bsp (- binding-value-slot binding-size))
(storew 0 bsp (- binding-symbol-slot binding-size))
+ (storew 0 bsp (- binding-value-slot binding-size))
(inst sub bsp (* binding-size n-word-bytes))
(store-symbol-value bsp *binding-stack-pointer*)))
#!+sb-thread
(inst mov (make-ea :qword :base thread-base-tn :scale 1 :index tls-index)
value)
- (storew 0 bsp (- binding-value-slot binding-size))
(storew 0 bsp (- binding-symbol-slot binding-size))
SKIP
+ (storew 0 bsp (- binding-value-slot binding-size))
(inst sub bsp (* binding-size n-word-bytes))
(inst cmp where bsp)
(inst jmp :ne LOOP)
(inst fs-segment-prefix)
(inst mov (make-ea :dword :base tls-index) value)
- (storew 0 bsp (- binding-value-slot binding-size))
(storew 0 bsp (- binding-symbol-slot binding-size))
+ (storew 0 bsp (- binding-value-slot binding-size))
(inst sub bsp (* binding-size n-word-bytes))
(store-binding-stack-pointer bsp)))
(loadw symbol bsp (- binding-symbol-slot binding-size))
(loadw value bsp (- binding-value-slot binding-size))
(storew value symbol symbol-value-slot other-pointer-lowtag)
- (storew 0 bsp (- binding-value-slot binding-size))
(storew 0 bsp (- binding-symbol-slot binding-size))
+ (storew 0 bsp (- binding-value-slot binding-size))
(inst sub bsp (* binding-size n-word-bytes))
(store-symbol-value bsp *binding-stack-pointer*)))
tls-index symbol symbol-tls-index-slot other-pointer-lowtag)
#!+sb-thread (inst fs-segment-prefix)
#!+sb-thread (inst mov (make-ea :dword :base tls-index) value)
- (storew 0 bsp (- binding-value-slot binding-size))
(storew 0 bsp (- binding-symbol-slot binding-size))
SKIP
+ (storew 0 bsp (- binding-value-slot binding-size))
(inst sub bsp (* binding-size n-word-bytes))
(inst cmp where bsp)
(inst jmp :ne loop)
--- /dev/null
+;;;; Tests for async signal safety.
+
+;;;; This software is part of the SBCL system. See the README file for
+;;;; more information.
+;;;;
+;;;; While most of SBCL is derived from the CMU CL system, the test
+;;;; files (like this one) were written from scratch after the fork
+;;;; from CMU CL.
+;;;
+;;;; This software is in the public domain and is provided with
+;;;; absoluely no warranty. See the COPYING and CREDITS files for
+;;;; more information.
+
+(use-package :test-util)
+
+(with-test (:name (:async-unwind :specials))
+ (let ((*x0* nil) (*x1* nil) (*x2* nil) (*x3* nil) (*x4* nil))
+ (declare (special *x0* *x1* *x2* *x3* *x4*))
+ (loop repeat 10 do
+ (loop repeat 10 do
+ (catch 'again
+ (sb-ext:schedule-timer (sb-ext:make-timer
+ (lambda ()
+ (throw 'again nil)))
+ (random 0.1))
+ (loop
+ (let ((*x0* (cons nil nil)) (*x1* (cons nil nil))
+ (*x2* (cons nil nil)) (*x3* (cons nil nil))
+ (*x4* (cons nil nil)))
+ (declare (special *x0* *x1* *x2* *x3* *x4*)))))
+ (when (not (and (null *x0*) (null *x1*) (null *x2*) (null *x3*)
+ (null *x4*)))
+ (format t "~S ~S ~S ~S ~S~%" *x0* *x1* *x2* *x3* *x4*)
+ (assert nil)))
+ (princ '*)
+ (force-output))
+ (terpri)))
;;; checkins which aren't released. (And occasionally for internal
;;; versions, especially for internal versions off the main CVS
;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"0.9.17.9"
+"0.9.17.10"