0.9.16.27:
[sbcl.git] / src / code / target-hash-table.lisp
index 138d39e..51d6537 100644 (file)
 \f
 ;;;; utilities
 
+;; This stuff is performance critical and unwind-protect is too
+;; slow. And without the locking the next vector can get cyclic
+;; causing looping in a WITHOUT-GCING form, SHRINK-VECTOR can corrupt
+;; memory and who knows what else.
+(defmacro with-spinlock-and-without-gcing ((spinlock) &body body)
+  #!-sb-thread
+  (declare (ignore spinlock))
+  (with-unique-names (old-gc-inhibit)
+    `(let ((,old-gc-inhibit *gc-inhibit*)
+           (*gc-inhibit* t))
+       (unwind-protect
+            (progn
+              #!+sb-thread
+              (sb!thread::get-spinlock ,spinlock)
+              ,@body)
+         #!+sb-thread
+         (sb!thread::release-spinlock ,spinlock)
+         (let ((*gc-inhibit* ,old-gc-inhibit))
+           ;; the test is racy, but it can err only on the overeager side
+           (sb!kernel::maybe-handle-pending-gc))))))
+
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defconstant max-hash sb!xc:most-positive-fixnum))
 
                    :weak-p weak-p
                    :index-vector index-vector
                    :next-vector next-vector
-                   :hash-vector (unless (eq test 'eq)
-                                  (make-array size+1
-                                              :element-type '(unsigned-byte #.sb!vm:n-word-bits)
-                                              :initial-element +magic-hash-vector-value+)))))
+                   :hash-vector
+                   (unless (eq test 'eq)
+                     (make-array size+1
+                                 :element-type '(unsigned-byte
+                                                 #.sb!vm:n-word-bits)
+                                 :initial-element +magic-hash-vector-value+))
+                   :spinlock (sb!thread::make-spinlock))))
       (declare (type index size+1 scaled-size length))
       ;; Set up the free list, all free. These lists are 0 terminated.
       (do ((i 1 (1+ i)))
                (the index (truncate (* rehash-size old-size)))))))
          (new-kv-vector (make-array (* 2 new-size)
                                     :initial-element +empty-ht-slot+))
-         (new-next-vector (make-array new-size
-                                      :element-type '(unsigned-byte #.sb!vm:n-word-bits)
-                                      :initial-element 0))
-         (new-hash-vector (when old-hash-vector
-                            (make-array new-size
-                                        :element-type '(unsigned-byte #.sb!vm:n-word-bits)
-                                        :initial-element +magic-hash-vector-value+)))
+         (new-next-vector
+          (make-array new-size
+                      :element-type '(unsigned-byte #.sb!vm:n-word-bits)
+                      :initial-element 0))
+         (new-hash-vector
+          (when old-hash-vector
+            (make-array new-size
+                        :element-type '(unsigned-byte #.sb!vm:n-word-bits)
+                        :initial-element +magic-hash-vector-value+)))
          (old-index-vector (hash-table-index-vector table))
          (new-length (almost-primify
                       (truncate (/ (float new-size)
                                 (hash-table-rehash-threshold table)))))
-         (new-index-vector (make-array new-length
-                                       :element-type '(unsigned-byte #.sb!vm:n-word-bits)
-                                       :initial-element 0)))
+         (new-index-vector
+          (make-array new-length
+                      :element-type '(unsigned-byte #.sb!vm:n-word-bits)
+                      :initial-element 0)))
     (declare (type index new-size new-length old-size))
 
     ;; Disable GC tricks on the OLD-KV-VECTOR.
                      (hash-table-next-free-kv table))
                (setf (hash-table-next-free-kv table) i))
               ((and new-hash-vector
-                    (not (= (aref new-hash-vector i) +magic-hash-vector-value+)))
+                    (not (= (aref new-hash-vector i)
+                            +magic-hash-vector-value+)))
                ;; Can use the existing hash value (not EQ based)
                (let* ((hashing (aref new-hash-vector i))
                       (index (rem hashing new-length))
     (setf (hash-table-next-vector table) new-next-vector)
     (setf (hash-table-hash-vector table) new-hash-vector)
     ;; Shrink the old vectors to 0 size to help the conservative GC.
-    (shrink-vector old-kv-vector 0)
-    (shrink-vector old-index-vector 0)
-    (shrink-vector old-next-vector 0)
+    (%shrink-vector old-kv-vector 0)
+    (%shrink-vector old-index-vector 0)
+    (%shrink-vector old-next-vector 0)
     (when old-hash-vector
-      (shrink-vector old-hash-vector 0))
+      (%shrink-vector old-hash-vector 0))
     (setf (hash-table-rehash-trigger table) new-size))
   (values))
 
                ;; Slot is empty, push it onto free list.
                (setf (aref next-vector i) (hash-table-next-free-kv table))
                (setf (hash-table-next-free-kv table) i))
-              ((and hash-vector (not (= (aref hash-vector i) +magic-hash-vector-value+)))
+              ((and hash-vector (not (= (aref hash-vector i)
+                                        +magic-hash-vector-value+)))
                ;; Can use the existing hash value (not EQ based)
                (let* ((hashing (aref hash-vector i))
                       (index (rem hashing length))
   "Three argument version of GETHASH"
   (declare (type hash-table hash-table)
            (values t (member t nil)))
-  (without-gcing
+  (with-spinlock-and-without-gcing ((hash-table-spinlock hash-table))
    (cond ((= (get-header-data (hash-table-table hash-table))
              sb!vm:vector-must-rehash-subtype)
           (rehash-without-growing hash-table))
 (defun %puthash (key hash-table value)
   (declare (type hash-table hash-table))
   (aver (hash-table-index-vector hash-table))
-  (without-gcing
+  (with-spinlock-and-without-gcing ((hash-table-spinlock hash-table))
    ;; We need to rehash here so that a current key can be found if it
    ;; exists. Check that there is room for one more entry. May not be
    ;; needed if the key is already present.
          (kv-vector (hash-table-table hash-table)))
 
      ;; Check the cache
-     (if (and cache (< cache (length kv-vector)) (eq (aref kv-vector cache) key))
+     (if (and cache (< cache (length kv-vector))
+              (eq (aref kv-vector cache) key))
          ;; If cached, just store here
          (setf (aref kv-vector (1+ cache)) value)
 
 
            (cond ((or eq-based (not hash-vector))
                   (when eq-based
-                    (set-header-data kv-vector sb!vm:vector-valid-hashing-subtype))
+                    (set-header-data kv-vector
+                                     sb!vm:vector-valid-hashing-subtype))
 
                   ;; Search next-vector chain for a matching key.
                   (do ((next next (aref next-vector next)))
              (when hash-vector
                (if (not eq-based)
                    (setf (aref hash-vector free-kv-slot) hashing)
-                 (aver (= (aref hash-vector free-kv-slot) +magic-hash-vector-value+))))
+                   (aver (= (aref hash-vector free-kv-slot)
+                            +magic-hash-vector-value+))))
 
              ;; Push this slot into the next chain.
              (setf (aref next-vector free-kv-slot) next)
    was such an entry, or NIL if not."
   (declare (type hash-table hash-table)
            (values (member t nil)))
-  (without-gcing
+  (with-spinlock-and-without-gcing ((hash-table-spinlock hash-table))
    ;; We need to rehash here so that a current key can be found if it
    ;; exists.
    (cond ((= (get-header-data (hash-table-table hash-table))
                       (hash-table-next-free-kv hash-table))
                 (setf (hash-table-next-free-kv hash-table) slot-location)
                 (when hash-vector
-                  (setf (aref hash-vector slot-location) +magic-hash-vector-value+))
+                  (setf (aref hash-vector slot-location)
+                        +magic-hash-vector-value+))
                 (decf (hash-table-number-entries hash-table))
                 t))
          (cond ((zerop next)
                   (declare (type index next))
                   (when (and (= hashing (aref hash-vector next))
                              (funcall test-fun key (aref table (* 2 next))))
-                    (return-from remhash (clear-slot next-vector prior next)))))))))))
+                    (return-from remhash
+                      (clear-slot next-vector prior next)))))))))))
 
 (defun clrhash (hash-table)
   #!+sb-doc
   "This removes all the entries from HASH-TABLE and returns the hash table
    itself."
   (declare (optimize speed))
-  (let* ((kv-vector (hash-table-table hash-table))
-         (next-vector (hash-table-next-vector hash-table))
-         (hash-vector (hash-table-hash-vector hash-table))
-         (size (length next-vector))
-         (index-vector (hash-table-index-vector hash-table)))
-    ;; Disable GC tricks.
-    (set-header-data kv-vector sb!vm:vector-normal-subtype)
-    ;; Mark all slots as empty by setting all keys and values to magic
-    ;; tag.
-    (aver (eq (aref kv-vector 0) hash-table))
-    (fill kv-vector +empty-ht-slot+ :start 2)
-    ;; Set up the free list, all free.
-    (do ((i 1 (1+ i)))
-        ((>= i (1- size)))
-      (setf (aref next-vector i) (1+ i)))
-    (setf (aref next-vector (1- size)) 0)
-    (setf (hash-table-next-free-kv hash-table) 1)
-    (setf (hash-table-needing-rehash hash-table) 0)
-    ;; Clear the index-vector.
-    (fill index-vector 0)
-    ;; Clear the hash-vector.
-    (when hash-vector
-      (fill hash-vector +magic-hash-vector-value+)))
-  (setf (hash-table-cache hash-table) nil)
-  (setf (hash-table-number-entries hash-table) 0)
+  (with-spinlock-and-without-gcing ((hash-table-spinlock hash-table))
+    (let* ((kv-vector (hash-table-table hash-table))
+           (next-vector (hash-table-next-vector hash-table))
+           (hash-vector (hash-table-hash-vector hash-table))
+           (size (length next-vector))
+           (index-vector (hash-table-index-vector hash-table)))
+      ;; Disable GC tricks.
+      (set-header-data kv-vector sb!vm:vector-normal-subtype)
+      ;; Mark all slots as empty by setting all keys and values to magic
+      ;; tag.
+      (aver (eq (aref kv-vector 0) hash-table))
+      (fill kv-vector +empty-ht-slot+ :start 2)
+      ;; Set up the free list, all free.
+      (do ((i 1 (1+ i)))
+          ((>= i (1- size)))
+          (setf (aref next-vector i) (1+ i)))
+      (setf (aref next-vector (1- size)) 0)
+      (setf (hash-table-next-free-kv hash-table) 1)
+      (setf (hash-table-needing-rehash hash-table) 0)
+      ;; Clear the index-vector.
+      (fill index-vector 0)
+      ;; Clear the hash-vector.
+      (when hash-vector
+        (fill hash-vector +magic-hash-vector-value+)))
+    (setf (hash-table-cache hash-table) nil)
+    (setf (hash-table-number-entries hash-table) 0))
   hash-table)
 \f
 ;;;; MAPHASH
 
 (def!method print-object ((hash-table hash-table) stream)
   (declare (type stream stream))
-  (cond ((not *print-readably*)
+  (cond ((or (not *print-readably*) (not *read-eval*))
          (print-unreadable-object (hash-table stream :type t :identity t)
            (format stream
                    ":TEST ~S :COUNT ~S"
                    (hash-table-test hash-table)
                    (hash-table-count hash-table))))
-        ((not *read-eval*)
-         (error "can't print hash tables readably without *READ-EVAL*"))
         (t
          (with-standard-io-syntax
           (format stream