Mercurial > core / lisp/lib/obj/hash/castable.lisp
changeset 441: |
9fa3b9154bb2 |
parent: |
2b7f0c032fc7
|
child: |
6856c021d084 |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Thu, 13 Jun 2024 21:57:36 -0400 |
permissions: |
-rw-r--r-- |
description: |
add pod/containerfile, rm pod/buildah, some work on zstd |
1 ;;; lib/obj/hash/castable.lisp --- CAS Table 3 ;; This implementation was written by Shinmera: 4 ;; https://github.com/Shinmera/luckless/blob/master/cat.lisp 6 ;; It is based on the JVM implementation of some concurrent data 10 (in-package :obj/hash) 13 (defstruct (prime (:constructor prime (value))) 16 (defmethod make-load-form ((self prime) &optional environment) 17 (declare (ignore environment)) 20 (defconstant max-spin 2) 21 (defconstant reprobe-limit 10) 22 (defconstant min-size-log 3) 23 (defconstant min-size (ash 1 min-size-log)) 24 (defconstant no-match-old 'no-match-old) 25 (defconstant match-any 'match-any) 26 (defconstant tombstone 'tombstone) 27 (defconstant tombprime (if (boundp 'tombprime) tombprime (prime tombstone))) 28 (defconstant no-value 'no-value) 32 (:constructor %make-cat (next table)) 34 (resizers 0 :type fixnum) 36 (sum-cache most-negative-fixnum :type fixnum) 37 (fuzzy-sum-cache 0 :type fixnum) 38 (fuzzy-time 0 :type fixnum) 39 (table nil :type simple-vector)) 41 (defun make-cat (next size initial-element) 42 (declare (type fixnum initial-element)) 43 (declare (type fixnum size)) 44 (let ((table (make-array size :initial-element 0))) 45 (setf (aref table 0) initial-element) 46 (%make-cat next table))) 48 (declaim (ftype (function (cat fixnum) fixnum) cat-sum)) 49 ;; L199 long sum(long) 50 (defun cat-sum (cat mask) 51 (declare (type fixnum mask)) 52 (declare (optimize speed)) 53 (let ((sum (%cat-sum-cache cat))) 54 (cond ((/= most-negative-fixnum sum) 57 (setf sum (if (null (%cat-next cat)) 59 (cat-sum (%cat-next cat) mask))) 60 (let ((%t (%cat-table cat))) 61 (dotimes (i (length %t)) 62 (incf sum (logand (svref %t i) (lognot mask)))) 63 (setf (%cat-sum-cache cat) sum) 66 (declaim (ftype (function (cat fixnum) fixnum) cat-sum~)) 67 ;; L212 long estimate_sum(long) 68 (defun cat-sum~ (cat mask) 69 (declare (type fixnum mask)) 70 (declare (optimize speed)) 71 (cond ((<= (length (%cat-table cat)) 64) 74 (let ((millis (get-internal-real-time))) 75 (when (/= millis (%cat-fuzzy-time cat)) 76 (setf (%cat-fuzzy-sum-cache cat) (cat-sum cat mask)) 77 (setf (%cat-fuzzy-time cat) millis)) 78 (%cat-fuzzy-sum-cache cat))))) 81 (defstruct (counter (:constructor make-counter ()) 82 (:conc-name %counter-)) 83 ;; Why is this slot at L97, after all the methods? I almost missed it, reading 85 (cat (make-cat NIL 4 0) :type cat)) 87 (declaim (inline decf-counter)) 88 ;; L41 decrement(), but with a delta argument. 89 (defun decf-counter (counter &optional (delta 1)) 90 (declare (type fixnum delta)) 91 (counter-add-if-mask counter (- delta) 0)) 93 (declaim (inline incf-counter)) 94 ;; L43 increment(), but with a delta increment. 95 (defun incf-counter (counter &optional (delta 1)) 96 (declare (type fixnum delta)) 97 (counter-add-if-mask counter delta 0)) 100 (defun (setf counter-value) (x counter) 101 (declare (optimize speed)) 102 (declare (type fixnum x)) 103 (loop with new = (make-cat NIL 4 x) 104 until (sb-ext:cas (%counter-cat counter) (%counter-cat counter) new))) 106 (declaim (inline counter-value)) 108 (defun counter-value (counter) 109 (cat-sum (%counter-cat counter) 0)) 111 (declaim (inline counter-value~)) 112 ;; L69 estimate_get() 113 (defun counter-value~ (counter) 114 (cat-sum~ (%counter-cat counter) 0)) 116 ;; L150 add_if_mask(long, long, int, ConcurrentAutoTable) 117 ;; This is a method in the CAT in the Java implementation, but here the 118 ;; ConcurrentAutoTable (counter) is the object being acted upon. 119 (defun counter-add-if-mask (counter x mask) 120 (declare (type fixnum x mask)) 121 (declare (optimize speed)) 122 (let* ((cat (%counter-cat counter)) 123 (%t (%cat-table cat)) 124 (idx (logand *global-hash* (1- (length %t)))) 125 (old (the fixnum (svref %t idx))) 127 (ok (sb-ext:cas (svref %t idx) (logand old (lognot mask)) (+ old x)))) 128 (flet ((fail () (return-from counter-add-if-mask old))) 130 (when (/= (%cat-sum-cache cat) most-negative-fixnum) 131 (setf (%cat-sum-cache cat) most-negative-fixnum)) 133 (when (/= 0 (logand old mask)) (fail)) 136 (declare (type fixnum cnt)) 137 (loop (setf old (the fixnum (svref %t idx))) 138 (when (/= 0 (logand old mask)) (fail)) 139 (when (sb-ext:cas (svref %t idx) old (+ old x)) (return)) 141 ;; Make sure we don't spin too long 142 (when (< cnt MAX-SPIN) (fail)) 144 (when (<= (* 1024 1024) (length %t)) (fail)) 145 ;; We are contending too much, increase the size in hopes it'll help 146 (let ((r (%cat-resizers cat)) 147 (newbytes (ash (ash (length %t) 1) 4))) 148 (declare (type fixnum r newbytes)) 149 (loop while (not (sb-ext:cas (%cat-resizers cat) r (+ r newbytes))) 150 do (setf r (%cat-resizers cat))) 152 ;; Already doubled up, don't bother 153 (unless (eql cat (%counter-cat counter)) 155 ;; Did we try to allocate too often already? 156 (when (/= 0 (ash r -17)) 157 (sleep (/ (ash r -17) 1000)) 158 (unless (eql cat (%counter-cat counter)) 160 ;; Try to extend the CAT once, if it fails another thread 161 ;; already did it for us so we don't have to retry. 162 (let ((new (make-cat cat (* (length %t) 2) 0))) 163 (sb-ext:cas (%counter-cat counter) cat new) 167 (declaim (ftype (function (unsigned-byte) fixnum) rehash) 171 "Spread bits of the hash H around." 172 (declare (optimize speed)) 173 (declare (type (integer 0) h)) 174 (let ((h (logand h most-positive-fixnum))) 175 (declare (type (unsigned-byte 64) h)) 176 (incf h (logior (logand most-positive-fixnum (ash h 15)) #xffffcd7d)) 177 (setf h (logior h (ash h -10))) 178 (incf h (logand most-positive-fixnum (ash h 3))) 179 (setf h (logior h (ash h -6))) 180 (incf h (logand most-positive-fixnum (+ (ash h 2) (ash h 14)))) 181 (setf h (logior h (ash h -16))) 182 (logand h most-positive-fixnum))) 184 (declaim (inline reprobe-limit)) 185 (defun reprobe-limit (len) 186 (+ reprobe-limit (ash len -2))) 188 ;; L713, private class CHM 189 ;; "The control structure for the NonBlockingHashMap" 191 (:constructor make-chm (size)) 193 (size (error "no size?") :type counter) 194 (slots (make-counter) :type counter) 195 (newkvs NIL :type (or null simple-vector)) 196 (resizers 0 :type fixnum) 197 (copy-idx 0 :type fixnum) 198 (copy-done 0 :type fixnum)) 199 (declaim (ftype (function (chm) fixnum) 200 %chm-resizers %chm-copy-idx %chm-copy-done)) 203 (declaim (inline chm-size)) 204 (defun chm-size (chm) 205 (counter-value (%chm-size chm))) 208 (declaim (inline chm-slots)) 209 (defun chm-slots (chm) 210 (counter-value (%chm-slots chm))) 212 (declaim (inline cas-newkvs)) 213 ;; L742, boolean CAS_newkvs(Object[]) 214 (defun cas-newkvs (chm newkvs) 215 (loop while (null (%chm-newkvs chm)) 216 do (when (sb-ext:cas (%chm-newkvs chm) NIL newkvs) 218 finally (return NIL))) 220 ;; Heuristic to test if the table is too full and we should make a new one. 221 (declaim (inline table-full-p)) 222 ;; L780, boolean tableFull(int, int) 223 (defun table-full-p (chm reprobe-cnt len) 224 (and (<= REPROBE-LIMIT reprobe-cnt) 225 (<= (reprobe-limit len) (counter-value~ (%chm-slots chm))))) 228 (:constructor %make-castable (kvs last-resize test hasher)) 229 (:conc-name %castable-)) 230 (kvs (error "no KVS?") :type simple-vector) 231 (last-resize (error "no LAST-RESIZE?") :type fixnum) 232 (reprobes (make-counter) :type counter) 233 (test (error "no TEST?") :type (function (T T) boolean)) 234 (hasher (error "no HASHER?") :type (function (T) fixnum))) 236 (declaim (inline chm)) 237 (declaim (ftype (function (simple-vector) chm) chm)) 238 ;; L138, static CHM chm(Object[]) 242 (declaim (inline hashes)) 243 (declaim (ftype (function (simple-vector) (simple-array fixnum)) hashes)) 244 ;; L139, static int[] hashes(Object[]) 248 (declaim (inline len)) 249 (declaim (ftype (function (simple-vector) (unsigned-byte 32)) len)) 250 ;; L140, static int len(Object[]) 252 (the (unsigned-byte 32) (ash (- (length kvs) 2) -1))) 254 (declaim (inline key)) 255 (declaim (ftype (function (simple-vector (unsigned-byte 32)) T) key)) 256 ;; L175 static Object key(Object[], int) 258 (svref kvs (+ 2 (ash idx 1)))) 260 (declaim (inline val)) 261 (declaim (ftype (function (simple-vector (unsigned-byte 32)) T) val)) 262 ;; L176 static Object val(Object[], int) 264 (svref kvs (+ 3 (ash idx 1)))) 266 (declaim (inline cas-key)) 267 ;; L177 static boolean CAS_key(Object[], int, Object, Object) 268 (defun cas-key (kvs idx old key) 269 (declare (simple-vector kvs)) 270 (sb-ext:cas (svref kvs (+ 2 (ash idx 1))) old key)) 272 (declaim (inline cas-val)) 273 ;; L180 static boolean CAS_val(Object[], int, Object, Object) 274 (defun cas-val (kvs idx old val) 275 (declare (simple-vector kvs)) 276 (sb-ext:cas (svref kvs (+ 3 (ash idx 1))) old val)) 278 ;; L237 long reprobes() 279 (defun reprobes (table) 280 (prog1 (counter-value (%castable-reprobes table)) 281 (setf (%castable-reprobes table) (make-counter)))) 283 (defun determine-hasher (test) 284 (or (cond ((eq test #'eq) #'sb-impl::eq-hash) 285 ((eq test #'eql) #'sb-impl::eql-hash) 286 ((eq test #'equal) #'sb-impl::equal-hash) 287 ((eq test #'equalp) #'sb-impl::equalp-hash) 288 ;; FIXME: implement own equalp hash 289 (t (third (find test sb-impl::*user-hash-table-tests* :key #'second)))) 290 (error "Don't know a hasher for ~a." test))) 292 (defvar *maximum-size* (* 8 1024 1024)) 293 (defun make-castable (&key test size hash-function) 294 (let* ((size (min *maximum-size* (max MIN-SIZE (or size 0)))) 295 (test (etypecase test 298 (symbol (fdefinition test)))) 299 (hash-function (etypecase hash-function 300 (null (determine-hasher test)) 301 (function hash-function) 302 (symbol (fdefinition hash-function))))) 303 (let ((power-of-two (expt 2 (integer-length size)))) 304 (let ((kvs (make-array (+ 2 (ash power-of-two 1)) :initial-element NO-VALUE))) 305 (setf (svref kvs 0) (make-chm (make-counter))) 306 (setf (svref kvs 1) (make-array power-of-two :element-type 'fixnum :initial-element 0)) 307 (%make-castable kvs (get-internal-real-time) test hash-function))))) 309 (declaim (inline hash)) 310 (defun hash (table thing) 311 (rehash (funcall (%castable-hasher table) thing))) 313 ;; Not `int size()` from the original! This is more like HASH-TABLE-SIZE, as 314 ;; it is the number of mappings that can be held right now. 315 (defun castable-size (table) 316 (/ (- (length (%castable-kvs table)) 2) 2)) 319 (defun castable-count (table) 320 (chm-size (chm (%castable-kvs table)))) 322 (defun castable-test (table) 323 (%castable-test table)) 325 (defun castable-hasher (table) 326 (%castable-hasher table)) 328 ;; L321 TypeV putIfAbsent(TypeK, TypeV) 329 (defun put-if-absent (table key value) 330 (multiple-value-bind (out present?) 331 (put-if-match table key value TOMBSTONE) 332 (declare (ignore out)) 334 ;; Missing: containsValue 335 ;; L342 boolean replace(TypeK, TypeV) 336 (defun put-if-present (table key value) 337 (multiple-value-bind (out present?) 338 (put-if-match table key value MATCH-ANY) 340 (funcall (%castable-test table) out value) 342 ;; L347 boolean replace(TypeK, TypeV, TypeV) 343 (defun put-if-equal (table key new-value old-value) 344 (multiple-value-bind (out present?) 345 (put-if-match table key new-value old-value) 347 (funcall (%castable-test table) old-value out) 351 ;; L313 put(TypeK, TypeV) 352 (defun (setf getchash) (value key table &key (if-exists :overwrite) (if-does-not-exist :overwrite)) 355 (ecase if-does-not-exist 357 (put-if-match table key value NO-MATCH-OLD)) 359 (unless (put-if-present table key value) 360 (error "Key does not exist in table."))) 362 (put-if-present table key value)))) 364 (ecase if-does-not-exist 366 (unless (put-if-absent table key value) 367 (error "Key already exists in table."))) 369 (error "Key either does or does not exist in table.")) 371 (when (nth-value 1 (getchash key table)) 372 (error "Key already exists in table."))))) 374 (ecase if-does-not-exist 376 (put-if-absent table key value)) 378 (unless (nth-value 1 (getchash key table)) 379 (error "Key does not exist in table."))) 384 (define-compiler-macro (setf getchash) (value key table &key (if-exists :overwrite) (if-does-not-exist :create)) 385 (let ((v (gensym "VALUE"))) 389 (ecase if-does-not-exist 391 `(put-if-match ,table ,key ,v NO-MATCH-OLD)) 393 `(unless (put-if-present ,table ,key ,v) 394 (error "Key does not exist in table."))) 396 `(put-if-present ,table ,key ,v)))) 398 (ecase if-does-not-exist 400 `(unless (put-if-absent ,table ,key ,v) 401 (error "Key already exists in table."))) 403 `(error "Key either does or does not exist in table.")) 405 `(when (nth-value 1 (getchash ,key ,table)) 406 (error "Key already exists in table."))))) 408 (ecase if-does-not-exist 410 `(put-if-absent ,table ,key ,v)) 412 `(unless (nth-value 1 (getchash ,key ,table)) 413 (error "Key does not exist in table."))) 418 ;; Close to L329 TypeV remove(Object) 419 ;; REMHASH returns true if there was a mapping and false otherwise, but 420 ;; remove() returns `null` or the old value. 421 (defun remchash (key table) 422 (if (eq TOMBSTONE (%put-if-match table (%castable-kvs table) key TOMBSTONE NO-MATCH-OLD)) 426 ;; Close to L334 boolean remove(Object, Object) 427 (defun try-remchash (table key val) 428 (multiple-value-bind (out present?) 429 (put-if-match table key TOMBSTONE val) 431 (funcall (%castable-test table) out val) 433 ;; L352 TypeV putIfMatch(Object, Object, Object) 434 (defun put-if-match (table key new old) 435 (let ((res (%put-if-match table (%castable-kvs table) key new old))) 436 (assert (not (prime-p res))) 437 (assert (not (eq res NO-VALUE))) 438 (if (eq res TOMBSTONE) 442 (defun clrchash (table) 443 (let ((new (%castable-kvs (make-castable)))) 444 (loop until (sb-ext:cas (%castable-kvs table) (%castable-kvs table) new)))) 446 (declaim (inline keyeq)) 447 ;; L467 boolean keyeq(Object, Object, int[], int, int) 448 (defun keyeq (k key hashes hash fullhash test) 449 (declare (type fixnum hash fullhash)) 450 (declare (type (function (T T) boolean) test)) 451 (declare (type (simple-array fixnum (*)) hashes)) 452 (declare (optimize speed)) 454 ;; Key does not match exactly, so try more expensive comparison. 455 (and ;; If the hash exists, does it match? 456 (or (= (aref hashes hash) 0) 457 (= (aref hashes hash) fullhash)) 458 ;; Avoid testing tombstones 459 (not (eq k TOMBSTONE)) 460 ;; Call test function for real comparison 461 (funcall test key k)))) 462 ;; L502 Object get_impl(NonBlockingHashMap, Object[], Object, int) 463 (defun %getchash (table kvs key fullhash) 464 (declare (type castable table)) 465 (declare (type simple-vector kvs)) 466 (declare (type fixnum fullhash)) 467 (declare (optimize speed)) 468 (let* ((len (len kvs)) 470 (hashes (hashes kvs)) 471 (idx (logand fullhash (1- len))) 472 (test (%castable-test table)) 474 (declare (fixnum reprobe-cnt)) 476 (loop (let ((k (key kvs idx)) 479 (when (eq k NO-VALUE) (return NO-VALUE)) 480 (let ((newkvs (%chm-newkvs chm))) 482 (when (keyeq k key hashes idx fullhash test) 483 ;; If we are not copying at the moment, we're done. 485 (return (if (eq v TOMBSTONE) 488 ;; Copy in progress, help with copying and retry. 489 (return (%getchash table 490 (copy-slot-and-check chm table kvs idx key) 493 ;; If we exceed reprobes, help resizing. 494 (when (or (<= (reprobe-limit len) (incf reprobe-cnt)) 499 ;; Retry in a new table copy 500 (return (%getchash table (help-copy table newkvs) key fullhash)))) 502 (setf idx (logand (1+ idx) (1- len)))))))) 504 ;; L495 TypeV get(Object) 505 (defun getchash (key table &optional default) 506 (declare (optimize speed)) 507 (let* ((fullhash (hash table key)) 508 (value (%getchash table (%castable-kvs table) key fullhash))) 509 ;; Make sure we never return primes 510 (check-type value (not prime)) 511 (if (eql value NO-VALUE) 515 ;; L555 Object putIfMatch(NonBlockingHashMap, Object[], Object, Object, Object) 516 (defun %put-if-match (table kvs key put exp) 517 (declare (type castable table)) 518 (declare (type simple-vector kvs)) 519 (declare (optimize speed)) 520 (assert (and (not (prime-p put)) 521 (not (prime-p exp)))) 522 (let* ((fullhash (hash table key)) 525 (hashes (hashes kvs)) 526 (test (%castable-test table)) 527 (idx (logand fullhash (1- len))) 529 (k NO-VALUE) (v NO-VALUE) 531 (declare (type fixnum idx reprobe-cnt)) 533 (loop (setf v (val kvs idx)) 534 (setf k (key kvs idx)) 536 (when (eq k NO-VALUE) 537 ;; No need to put a tombstone in an empty field 538 (when (eq put TOMBSTONE) 539 (return-from %put-if-match put)) 541 (when (cas-key kvs idx NO-VALUE key) 542 (incf-counter (%chm-slots chm)) 543 (setf (aref hashes idx) fullhash) 545 ;; We failed, update the key 546 (setf k (key kvs idx)) 547 (assert (not (eq k NO-VALUE)))) 548 ;; Okey, we have a key there 549 (setf newkvs (%chm-newkvs chm)) 550 ;; Test if this is our key 551 (when (keyeq k key hashes idx fullhash test) 553 ;; If we exceed reprobes, start resizing 554 (when (or (<= (reprobe-limit len) (incf reprobe-cnt)) 556 (setf newkvs (resize chm table kvs)) 557 (unless (eq exp NO-VALUE) (help-copy table newkvs)) 558 (return-from %put-if-match 559 (%put-if-match table newkvs key put exp))) 561 (setf idx (logand (1+ idx) (1- len)))) 562 ;; We found a key slot, time to update it 564 (when (eq put v) (return-from %put-if-match v)) 565 ;; Check if we want to move to a new table 566 (when (and ;; Do we have a new table already? 569 (or (and (eq v NO-VALUE) (table-full-p chm reprobe-cnt len)) 571 (setf newkvs (resize chm table kvs))) 572 ;; Check if we are indeed moving and retry 573 (unless (null newkvs) 574 (return-from %put-if-match 575 (%put-if-match table (copy-slot-and-check chm table kvs idx exp) key put exp))) 576 ;; Finally we can do the update 577 (loop (check-type v (not prime)) 578 ;; If we don't match the old, bail out 579 (when (and (not (eq exp NO-MATCH-OLD)) 581 (or (not (eq exp MATCH-ANY)) 584 (not (and (eq v NO-VALUE) (eq exp TOMBSTONE))) 585 (or (eq exp NO-VALUE) (not (funcall test exp v)))) 587 ;; Perform the change 588 (when (cas-val kvs idx v put) 589 ;; Okey, we got it, update the size 590 (unless (eq exp NO-VALUE) 591 (when (and (or (eq v NO-VALUE) (eq v TOMBSTONE)) 592 (not (eq put TOMBSTONE))) 593 (incf-counter (%chm-size chm))) 594 (when (and (not (or (eq v NO-VALUE) (eq v TOMBSTONE))) 596 (decf-counter (%chm-size chm)))) 597 (return (if (and (eq v NO-VALUE) (not (eq exp NO-VALUE))) 601 (setf v (val kvs idx)) 602 ;; If we got a prime we need to restart from the beginning 604 (return (%put-if-match table (copy-slot-and-check chm table kvs idx exp) key put exp)))))) 606 ;; L699 Object[] help_copy(Object[]) 607 (declaim (inline help-copy)) 608 (defun help-copy (table helper) 609 (declare (type castable table)) 610 (declare (optimize speed)) 611 (let* ((topkvs (%castable-kvs table)) 612 (topchm (chm topkvs))) 613 (unless (null (%chm-newkvs topchm)) 614 (%help-copy topchm table topkvs NIL)) 617 ;; L794 Object[] resize(NonBlockingHashMap, Object[]) 618 (defun resize (chm table kvs) 619 (declare (type chm chm)) 620 (declare (type castable table)) 621 (declare (type simple-array kvs)) 622 (declare (optimize speed)) 623 (assert (eq chm (chm kvs))) 624 ;; Check for resize in progress 625 (let ((newkvs (%chm-newkvs chm))) 626 (unless (null newkvs) 627 ;; Use the new table already 628 (return-from resize newkvs)) 629 (let* ((oldlen (len kvs)) 632 (declare (type fixnum oldlen sz newsz)) 633 ;; Heuristic for new size 634 (when (<= (ash oldlen -2) sz) 635 (setf newsz (ash oldlen 1)) 636 (when (<= sz (ash oldlen -1)) 637 (setf newsz (ash oldlen 2)))) 638 ;; Much denser table with more reprobes 640 (when (<= (ash oldlen -1) sz) 641 (setf newsz (ash oldlen 1))) 642 ;; Was the last resize recent? If so, double again 643 ;; to accommodate tables with lots of inserts at the moment. 644 (let ((tm (get-internal-real-time))) 645 (when (and (<= newsz oldlen) 646 ;; If we resized less than a second ago 647 (<= tm (+ (%castable-last-resize table) 648 INTERNAL-TIME-UNITS-PER-SECOND)) 649 ;; And we have plenty of dead keys 650 (<= (ash sz 1) (counter-value~ (%chm-slots chm)))) 651 (setf newsz (ash oldlen 1)))) 653 (when (< newsz oldlen) (setf newsz oldlen)) 654 (let ((size MIN-SIZE) 655 (r (%chm-resizers chm))) 656 (declare (type fixnum size)) 657 ;; Convert to power of two 658 (loop while (< size newsz) 659 do (setf size (ash size 1))) 660 ;; Limit the number of threads resizing things 661 (loop until (sb-ext:cas (%chm-resizers chm) r (1+ r)) 662 do (setf r (%chm-resizers chm))) 663 ;; Size calculation: 2 words per table + extra 664 ;; NOTE: The original assumes 32 bit pointers, we conditionalise 665 (let ((megs (ash (ash (+ (* size 2) 4) 666 #+64-BIT 4 #-64-BIT 3) 668 (declare (type fixnum megs)) 669 (when (and (<= 2 r) (< 0 megs)) 670 (setf newkvs (%chm-newkvs chm)) 671 (unless (null newkvs) 672 (return-from resize newkvs)) 673 ;; We already have two threads trying a resize, wait 674 (sleep (/ (* 8 megs) 1000)))) 676 (setf newkvs (%chm-newkvs chm)) 677 (unless (null newkvs) 678 (return-from resize newkvs)) 679 ;; Allocate the array 680 (setf newkvs (make-array (+ 2 (* 2 size)) :initial-element NO-VALUE)) 681 (setf (svref newkvs 0) (make-chm (%chm-size chm))) 682 (setf (svref newkvs 1) (make-array size :element-type 'fixnum :initial-element 0)) 683 ;; Check again after the allocation 684 (unless (null (%chm-newkvs chm)) 685 (return-from resize (%chm-newkvs chm))) 686 ;; CAS the table in. We can let the GC handle deallocation. Thanks, GC! 687 (if (cas-newkvs chm newkvs) 689 (%chm-newkvs chm)))))) 691 ;; L906 help_copy_impl(NonBlockingHashMap, Object[], boolean) 692 (defun %help-copy (chm table oldkvs copy-all) 693 (declare (type chm chm)) 694 (declare (type castable table)) 695 (declare (type simple-array oldkvs)) 696 (declare (optimize speed)) 697 (assert (eq chm (chm oldkvs))) 698 (let* ((newkvs (%chm-newkvs chm)) 699 (oldlen (len oldkvs)) 700 (min-copy-work (min oldlen 1024)) 703 (declare (type fixnum oldlen min-copy-work copy-idx panic-start)) 704 (assert (not (null newkvs))) 705 ;; Loop while there's work to be done 706 (loop while (< (%chm-copy-done chm) oldlen) 707 do ;; We panic if we tried to copy twice and it failed. 708 (when (= -1 panic-start) 709 (setf copy-idx (%chm-copy-idx chm)) 710 (loop while (and (< copy-idx (ash oldlen 1)) 711 (not (sb-ext:cas (%chm-copy-idx chm) copy-idx (+ copy-idx min-copy-work)))) 712 do (setf copy-idx (%chm-copy-idx chm))) 713 (unless (< copy-idx (ash oldlen 1)) 714 (setf panic-start copy-idx))) 715 ;; Okey, now perform the copy. 717 (declare (type fixnum workdone)) 718 (dotimes (i min-copy-work) 719 (when (copy-slot table (logand (+ copy-idx i) (1- oldlen)) 723 (when (plusp workdone) 724 (copy-check-and-promote chm table oldkvs workdone)) 725 (incf copy-idx min-copy-work) 726 ;; End early if we shouldn't copy everything. 727 (when (and (not copy-all) (= -1 panic-start)) 728 (return-from %help-copy)))) 729 ;; Promote again in case we race on end of copy 730 (copy-check-and-promote chm table oldkvs 0))) 732 ;; Copy the slot and check that we have done so successfully. 733 ;; L970 Object[] copy_slot_and_check(NonBlockingHashMap, Object[], int, Object) 734 (defun copy-slot-and-check (chm table oldkvs idx should-help) 735 (declare (type chm chm)) 736 (declare (type castable table)) 737 (declare (type simple-array oldkvs)) 738 (declare (optimize speed)) 739 (assert (eq chm (chm oldkvs))) 740 (let ((newkvs (%chm-newkvs chm))) 741 (assert (not (null newkvs))) 742 (when (copy-slot table idx oldkvs (%chm-newkvs chm)) 743 (copy-check-and-promote chm table oldkvs 1)) 745 (help-copy table newkvs) 748 ;; L983 copy_check_and_promote(NonBlockingHashMap, Object[], int) 749 (defun copy-check-and-promote (chm table oldkvs work-done) 750 (declare (type chm chm)) 751 (declare (type castable table)) 752 (declare (type simple-array oldkvs)) 753 (declare (type fixnum work-done)) 754 (declare (optimize speed)) 755 (assert (eq chm (chm oldkvs))) 756 (let ((oldlen (len oldkvs)) 757 (copy-done (%chm-copy-done chm))) 758 (assert (<= (+ copy-done work-done) oldlen)) 759 (when (< 0 work-done) 760 (loop until (sb-ext:cas (%chm-copy-done chm) copy-done (+ copy-done work-done)) 761 do (setf copy-done (%chm-copy-done chm)) 762 (assert (<= (+ copy-done work-done) oldlen)))) 763 ;; Check for copy being completely done and promote 764 (when (and (= (+ copy-done work-done) oldlen) 765 (eq (%castable-kvs table) oldkvs) 766 (sb-ext:cas (%castable-kvs table) oldkvs (%chm-newkvs chm))) 767 (setf (%castable-last-resize table) (get-internal-real-time))))) 769 ;; Copy one slot into the new table, 770 ;; returns true if we are sure that the new table has a value. 771 ;; L1023 boolean copy_slot(NonBlockingHashMap, int, Object[], Object[]) 772 (defun copy-slot (table idx oldkvs newkvs) 773 (declare (type castable table)) 774 (declare (type fixnum idx)) 775 (declare (type simple-array oldkvs newkvs)) 776 (declare (optimize speed)) 777 ;; First tombstone the key blindly. 779 (loop while (eq (setf key (key oldkvs idx)) NO-VALUE) 780 do (cas-key oldkvs idx NO-VALUE TOMBSTONE)) 781 ;; Prevent new values from showing up in the old table 782 (let ((oldval (val oldkvs idx))) 783 (loop until (prime-p oldval) 784 for box = (if (or (eq oldval NO-VALUE) 785 (eq oldval TOMBSTONE)) 788 do (when (cas-val oldkvs idx oldval box) 789 ;; We made sure to prime the value to prevent updates. 790 (when (eq box TOMBPRIME) 791 (return-from copy-slot T)) 794 ;; Retry on CAS failure 795 (setf oldval (val oldkvs idx))) 796 (when (eq oldval TOMBPRIME) 797 ;; We already completed the copy 798 (return-from copy-slot NIL)) 799 ;; Finally do the actual copy, but only if we would write into 800 ;; a null. Otherwise, someone else already copied. 801 (let ((old-unboxed (prime-value oldval))) 802 (assert (not (eq old-unboxed TOMBSTONE))) 803 (prog1 (eq NO-VALUE (%put-if-match table newkvs key old-unboxed NO-VALUE)) 804 ;; Now that the copy is done, we can stub out the old key completely. 805 (loop until (cas-val oldkvs idx oldval TOMBPRIME) 806 do (setf oldval (val oldkvs idx)))))))) 808 (defun mapchash (function table) 810 (loop for top-kvs = (%castable-kvs table) 811 for top-chm = (chm top-kvs) 812 for newkvs = (%chm-newkvs top-chm) 814 do (help-copy table newkvs) 815 finally (setf snapshot-kvs top-kvs)) 816 (loop for position from 2 below (length snapshot-kvs) by 2 817 for key = (aref snapshot-kvs position) 818 for value = (aref snapshot-kvs (1+ position)) 819 unless (or (eq key NO-VALUE) 822 do (funcall function key value)))) 824 (defmethod print-object ((table castable) stream) 825 (print-unreadable-object (table stream :type t :identity t) 826 (format stream ":test ~s :count ~s :size ~s" 827 (castable-test table) 828 (castable-count table) 829 (castable-size table))))