;;;; -*- coding: utf-8; -*-
- * reduced conservativism on GENCGC platforms: on average 45% less
- pages pinned (measured from SBCL self build).
+ * enhancement: reduced conservativism on GENCGC platforms: on
+ average 45% less pages pinned (measured from SBCL self build).
+ * bug fix: SB-EXT:COMPARE-AND-SWAP on SYMBOL-VALUE can no longer
+ mutate constant symbols or violate declaimed type of the symbol.
changes in sbcl-1.0.20 relative to 1.0.19:
* minor incompatible change: OPTIMIZE qualities
(- (* ,(+ sb!vm:instance-slots-offset index) sb!vm:n-word-bytes)
sb!vm:instance-pointer-lowtag)))))))
-(defmacro compare-and-swap (place old new)
+(defmacro compare-and-swap (place old new &environment env)
"Atomically stores NEW in PLACE if OLD matches the current value of PLACE.
Two values are considered to match if they are EQ. Returns the previous value
of PLACE: if the returned value if EQ to OLD, the swap was carried out.
((cdr rest)
`(%compare-and-swap-cdr (the cons ,@args) ,old ,new))
(symbol-plist
- `(%compare-and-swap-symbol-plist (the symbol ,@args) ,old ,new))
+ `(%compare-and-swap-symbol-plist (the symbol ,@args) ,old (the list ,new)))
(symbol-value
- `(%compare-and-swap-symbol-value (the symbol ,@args) ,old ,new))
+ (destructuring-bind (name) args
+ (flet ((slow (symbol)
+ (with-unique-names (n-symbol n-old n-new)
+ `(let ((,n-symbol ,symbol)
+ (,n-old ,old)
+ (,n-new ,new))
+ (declare (symbol ,n-symbol))
+ (about-to-modify-symbol-value ,n-symbol "compare-and-swap SYMBOL-VALUE of ~S" ,n-new)
+ (%compare-and-swap-symbol-value ,n-symbol ,n-old ,n-new)))))
+ (if (sb!xc:constantp name env)
+ (let ((cname (constant-form-value name env)))
+ (if (eq :special (info :variable :kind cname))
+ ;; Since we know the symbol is a special, we can just generate
+ ;; the type check.
+ `(%compare-and-swap-symbol-value
+ ',cname ,old (the ,(info :variable :type cname) ,new))
+ (slow (list 'quote cname))))
+ (slow name)))))
(svref
(let ((vector (car args))
(index (cadr args)))
(ignore-errors (compare-and-swap (svref "foo" 1) 1 2))
(assert (not res))
(assert (typep err 'type-error)))
+
+;; Check that we don't modify constants
+(defconstant +a-constant+ 42)
+(assert
+ (eq :error
+ (handler-case
+ (sb-ext:compare-and-swap (symbol-value '+a-constant+) 42 13)
+ (error () :error))))
+(let ((name '+a-constant+))
+ (assert
+ (eq :error
+ (handler-case
+ (sb-ext:compare-and-swap (symbol-value name) 42 13)
+ (error () :error)))))
+
+;; Check that we don't mess declaimed types
+(declaim (boolean *a-boolean*))
+(defparameter *a-boolean* t)
+(assert
+ (eq :error
+ (handler-case
+ (sb-ext:compare-and-swap (symbol-value '*a-boolean*) t 42)
+ (error () :error))))
+(let ((name '*a-boolean*))
+ (assert
+ (eq :error
+ (handler-case
+ (sb-ext:compare-and-swap (symbol-value name) t 42)
+ (error () :error)))))