1.0.20.8: ATOMIC-INCF implementation
[sbcl.git] / src / code / late-extensions.lisp
index 1918c87..2e0a9df 100644 (file)
@@ -66,7 +66,7 @@
              (- (* ,(+ 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.
@@ -92,9 +92,26 @@ EXPERIMENTAL: Interface subject to change."
       ((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)))
@@ -124,6 +141,8 @@ EXPERIMENTAL: Interface subject to change."
              (error "Invalid first argument to COMPARE-AND-SWAP: ~S" place))))))))
 
 (macrolet ((def (name lambda-list ref &optional set)
+             #!+compare-and-swap-vops
+             (declare (ignore ref set))
              `(defun ,name (,@lambda-list old new)
                 #!+compare-and-swap-vops
                 (,name ,@lambda-list old new)
@@ -141,6 +160,61 @@ EXPERIMENTAL: Interface subject to change."
   (def %compare-and-swap-symbol-value (symbol) symbol-value)
   (def %compare-and-swap-svref (vector index) svref))
 
+(defmacro atomic-incf (place &optional (diff 1) &environment env)
+  #!+sb-doc
+  "Atomically increments PLACE by DIFF, and returns the value of PLACE before
+the increment.
+
+The incrementation is done using word-size modular arithmetic: on 32 bit
+platforms ATOMIC-INCF of #xFFFFFFFF by one results in #x0 being stored in
+PLACE.
+
+PLACE must be an accessor form whose CAR is the name of a DEFSTRUCT accessor
+whose declared type is (UNSIGNED-BYTE 32) on 32 bit platforms,
+and (UNSIGNED-BYTE 64) on 64 bit platforms.
+
+DIFF defaults to 1, and must be a (SIGNED-BYTE 32) on 32 bit platforms,
+and (SIGNED-BYTE 64) on 64 bit platforms.
+
+EXPERIMENTAL: Interface subject to change."
+  (flet ((invalid-place ()
+           (error "Invalid first argument to ATOMIC-INCF: ~S" place)))
+    (let ((place (macroexpand place env)))
+      (unless (consp place)
+        (invalid-place))
+      (destructuring-bind (op &rest args) place
+        (when (cdr args)
+          (invalid-place))
+        (let ((dd (info :function :structure-accessor op)))
+          (if dd
+              (let* ((structure (dd-name dd))
+                     (slotd (find op (dd-slots dd) :key #'dsd-accessor-name))
+                     (index (dsd-index slotd))
+                     (type (dsd-type slotd)))
+                (declare (ignorable index))
+                (unless (and (eq 'sb!vm:word (dsd-raw-type slotd))
+                             (type= (specifier-type type) (specifier-type 'sb!vm:word)))
+                  (error "ATOMIC-INCF requires a slot of type (UNSIGNED-BYTE ~S), not ~S: ~S"
+                         sb!vm:n-word-bits type place))
+                (when (dsd-read-only slotd)
+                  (error "Cannot use ATOMIC-INCF with structure accessor for a read-only slot: ~S"
+                         place))
+                #!+(or x86 x86-64)
+                `(truly-the sb!vm:word
+                            (%raw-instance-atomic-incf/word (the ,structure ,@args)
+                                                            ,index
+                                                            (the sb!vm:signed-word ,diff)))
+                ;; No threads outside x86 and x86-64 for now, so this is easy...
+                #!-(or x86 x86-64)
+                (with-unique-names (structure old)
+                  `(sb!sys:without-interrupts
+                     (let* ((,structure ,@args)
+                            (,old (,op ,structure)))
+                       (setf (,op ,structure) (logand #.(1- (ash 1 sb!vm:n-word-bits))
+                                                      (+ ,old (the sb!vm:signed-word ,diff))))
+                       ,old))))
+              (invalid-place)))))))
+
 (defun call-hooks (kind hooks &key (on-error :error))
   (dolist (hook hooks)
     (handler-case