[rucksack-cvs] CVS rucksack

alemmens alemmens at common-lisp.net
Fri Aug 4 22:04:43 UTC 2006


Update of /project/rucksack/cvsroot/rucksack
In directory clnet:/tmp/cvs-serv27309

Modified Files:
	objects.lisp p-btrees.lisp package.lisp test.lisp 
Log Message:
Clean up btree code.  Add BTREE-DELETE.  (From Edi Weitz.)


--- /project/rucksack/cvsroot/rucksack/objects.lisp	2006/05/24 20:45:09	1.4
+++ /project/rucksack/cvsroot/rucksack/objects.lisp	2006/08/04 22:04:43	1.5
@@ -1,4 +1,4 @@
-;; $Id: objects.lisp,v 1.4 2006/05/24 20:45:09 alemmens Exp $
+;; $Id: objects.lisp,v 1.5 2006/08/04 22:04:43 alemmens Exp $
 
 (in-package :rucksack)
 
@@ -265,10 +265,10 @@
            :end1 end1
            :start2 start2
            :end2 end2)
-  ;; DO: WE MUST TOUCH THE OBJECT HERE!!
+  ;; Touch the vector because it has changed.
+  (cache-touch-object vector-1 (cache vector-1))
   vector-1)
 
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Full fledged persistent objects
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
--- /project/rucksack/cvsroot/rucksack/p-btrees.lisp	2006/08/04 11:06:04	1.6
+++ /project/rucksack/cvsroot/rucksack/p-btrees.lisp	2006/08/04 22:04:43	1.7
@@ -1,15 +1,16 @@
-;; $Id: p-btrees.lisp,v 1.6 2006/08/04 11:06:04 alemmens Exp $
+;; $Id: p-btrees.lisp,v 1.7 2006/08/04 22:04:43 alemmens Exp $
 
 (in-package :rucksack)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;; b-trees: API
+;;; Btrees: API
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 #|
    ;; Btrees
    #:btree
-   #:btree-key< #:btree-key= #:btree-value=
+   #:btree-key< #:btree-key<= #:btree-key= #:btree-key>= #:btree-key>
+   #:btree-value=
    #:btree-max-node-size #:btree-unique-keys-p
    #:btree-key-type #:btree-value-type
    #:btree-node-class
@@ -18,7 +19,7 @@
    #:btree-node
 
    ;; Functions
-   #:btree-search #:btree-insert #:map-btree
+   #:btree-search #:btree-insert #:btree-delete #:map-btree
 
    ;; Conditions
    #:btree-error #:btree-search-error #:btree-insertion-error
@@ -82,7 +83,6 @@
 
 (defclass btree ()
   ((key<   :initarg :key<   :reader btree-key<   :initform '<)
-   (key=   :initarg :key=   :reader btree-key=   :initform 'eql)
    (value= :initarg :value= :reader btree-value= :initform 'p-eql
            :documentation "This is only used for btrees with non-unique keys.")
    ;;
@@ -109,10 +109,10 @@
    (root :accessor btree-root))
   (:metaclass persistent-class))
 
-  
+
 (defmethod initialize-instance :around ((btree btree)
                                         &rest initargs
-                                        &key key< key= value=
+                                        &key key< value=
                                         &allow-other-keys)
   ;; It must be possible to save these btrees in the cache, but
   ;; that will not work for function objects because they can't be
@@ -120,12 +120,39 @@
   ;; name a function.  For program-independent databases you should
   ;; only use symbols from the COMMON-LISP package.
   (declare (ignore initargs))
-  (if (and (symbolp key<) (symbolp key=) (symbolp value=))
+  (if (and (symbolp key<) (symbolp value=))
     (call-next-method)
-    (error "The :key<, :key= and :value= initargs for persistent btrees
+    (error "The :key< and :value= initargs for persistent btrees
 must be symbols naming a function, otherwise they can't be saved on
 disk.")))
 
+;;
+;; Comparison functions that can be deduced from KEY< (because the
+;; btree keys have a total order).
+;;
+
+(defmethod btree-key= ((btree btree))
+  (let ((key< (btree-key< btree)))
+    (lambda (key1 key2)
+      (and (not (funcall key< key1 key2))
+           (not (funcall key< key2 key1))))))
+
+(defmethod btree-key>= ((btree btree))
+  (lambda (key1 key2)
+    (funcall (btree-key< btree) key2 key1)))
+
+(defmethod btree-key<= ((btree btree))
+  (let ((key< (btree-key< btree)))
+    (lambda (key1 key2)
+      (or (funcall key< key1 key2)
+          (not (funcall key< key2 key1))))))
+
+(defmethod btree-key> ((btree btree))
+  (let ((key< (btree-key< btree)))
+    (lambda (key1 key2)
+      (and (not (funcall key< key1 key2))
+           (funcall key< key2 key1)))))
+
 
 ;;
 ;; The next two classes are for internal use only, so we don't bother
@@ -140,7 +167,7 @@
 sorted by KEY<. No two keys can be the same.  For leaf nodes of btrees
 with non-unique-keys, the value part is actually a list of values.
 For intermediate nodes, the value is a child node.  All keys in the
-child node will be KEY< the child node's key in the parent node.")
+child node will be KEY<= the child node's key in the parent node.")
    (index-count :initform 0
                 :accessor btree-node-index-count
                 :documentation "The number of key/value pairs in the index vector.")
@@ -187,7 +214,7 @@
 (defmethod initialize-instance :after ((node btree-node)
                                        &key btree &allow-other-keys)
   (setf (btree-node-index node) (p-make-array (btree-max-node-size btree)
-                                                   :initial-element nil)
+                                              :initial-element nil)
         (btree-node-index-count node) 0))
 
 
@@ -195,6 +222,24 @@
   (print-unreadable-object (node stream :type t :identity t)
     (format stream "with ~D pairs" (btree-node-index-count node))))
 
+;;
+;; Debugging
+;;
+
+(defun display-node (node)
+  (pprint (node-as-cons node)))
+
+(defun node-as-cons (node)
+  (loop with index = (btree-node-index node)
+        with leaf-p = (btree-node-leaf-p node)
+        for i below (btree-node-index-count node)
+        for binding = (p-aref index i)
+        collect (list (binding-key binding)
+                      (if leaf-p
+                        (binding-value binding)
+                        (node-as-cons (binding-value binding))))))
+
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Search
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -211,7 +256,7 @@
 (defmethod btree-search (btree key &key (errorp t) (default-value nil))
   (restart-case 
       (if (slot-boundp btree 'root)
-          (node-search btree (slot-value btree 'root) key errorp default-value)
+          (node-search btree (btree-root btree) key errorp default-value)
         (not-found btree key errorp default-value))
     (use-value (value)
       :report (lambda (stream)
@@ -239,14 +284,23 @@
 ;;
 ;; Node-search
 ;;
+
+(defun find-binding-in-node (key node btree)
+  (let ((index-count (btree-node-index-count node)))
+    (and (plusp index-count)
+         (loop with array = (btree-node-index node)
+               with btree-key< = (btree-key< btree)
+               for i from 0 below index-count
+               for candidate = (p-aref array i)
+               for candidate-key = (binding-key candidate)
+               while (funcall btree-key< candidate-key key)
+               finally (when (funcall (btree-key= btree) key candidate-key)
+                         (return candidate))))))
   
 (defgeneric node-search (btree node key errorp default-value)
   (:method ((btree btree) (node btree-node) key errorp default-value)
    (if (btree-node-leaf-p node)
-       (let ((binding (p-find key (btree-node-index node)
-                                   :key #'binding-key
-                                   :test (btree-key= btree)
-                                   :end (btree-node-index-count node))))
+       (let ((binding (find-binding-in-node key node btree)))
          (if binding
              (binding-value binding)
            (not-found btree key errorp default-value)))
@@ -258,17 +312,15 @@
   "Returns the subnode that contains more information for the given key."
   ;; Find the first binding with a key >= the given key and return
   ;; the corresponding subnode.
-  ;; DO: We should probably use binary search for this.
-  (loop for i below (btree-node-index-count node)
-        do (let ((binding (node-binding node i)))
-             (cond ((= i (1- (btree-node-index-count node)))
-                    ;; We're at the last binding.
-                    (return-from find-subnode (binding-value binding)))
-                   ((funcall (btree-key< btree) key (binding-key binding))
-                    (let ((next-binding (node-binding node (1+ i))))
-                      (if (funcall (btree-key= btree) key (binding-key next-binding))
-                          (return-from find-subnode (binding-value next-binding))
-                        (return-from find-subnode (binding-value binding))))))))
+  ;; EFFICIENCY: We should probably use binary search for this.
+  (loop with btree-key< = (btree-key< btree)
+        with last-index = (1- (btree-node-index-count node))
+        for i to last-index
+        for binding = (node-binding node i)
+        when (or (= i last-index)
+                 (funcall btree-key< key (binding-key binding))
+                 (not (funcall btree-key< (binding-key binding) key)))
+        do (return-from find-subnode (binding-value binding)))
   (error "This shouldn't happen."))
 
 
@@ -292,7 +344,7 @@
            :expected-type (btree-value-type btree)))
   ;; Do the real work.
   (if (slot-boundp btree 'root)
-      (btree-node-insert btree (slot-value btree 'root) nil key value if-exists)
+      (btree-node-insert btree (btree-root btree) (list nil) key value if-exists)
     ;; Create a root.
     (let ((leaf (make-instance (btree-node-class btree)
                                :btree btree
@@ -301,7 +353,7 @@
       (let* ((empty-leaf (make-instance (btree-node-class btree)
                                         :btree btree
                                         :leaf-p t))
-             (root (make-root btree key empty-leaf 'key-irrelevant leaf)))
+             (root (make-root btree key leaf 'key-irrelevant empty-leaf)))
         (setf (btree-root btree) root))))
   ;; Return the inserted value.
   value)
@@ -314,6 +366,7 @@
                  (declare (ignore value))
                  (when prev-key
                    (unless (funcall (btree-key< btree) prev-key key)
+                     (display-node (btree-root btree))
                      (error "Btree inconsistency between ~D and ~D" prev-key key)))
                  (setq prev-key key)))))
                    
@@ -330,17 +383,13 @@
 ;; Node insert
 ;;
 
-(defgeneric btree-node-insert (btree node parent key value if-exists))
+(defgeneric btree-node-insert (btree node parent-stack key value if-exists))
 
-(defmethod btree-node-insert ((btree btree) (node btree-node) parent key value if-exists)
-  (cond ((node-almost-full-p btree node)
-         (split-btree-node btree node parent)
-         (btree-insert btree key value :if-exists if-exists))
-        ((btree-node-leaf-p node)
-         (leaf-insert btree node key value if-exists))
+(defmethod btree-node-insert ((btree btree) (node btree-node) parent-stack key value if-exists)
+  (cond ((btree-node-leaf-p node)
+         (leaf-insert btree node parent-stack key value if-exists))
         (t (let ((subnode (find-subnode btree node key)))
-             (btree-node-insert btree subnode node key value if-exists)))))
-
+             (btree-node-insert btree subnode (cons node parent-stack) key value if-exists)))))
 
 (defun smallest-key (node)
   (if (btree-node-leaf-p node)
@@ -353,50 +402,52 @@
     (biggest-key (binding-value (node-binding node (1- (btree-node-index-count node)))))))
 
 
-(defun split-btree-node (btree node parent)
+(defun split-btree-node (btree node parent-stack key)
   ;; The node is (almost) full.
   ;; Create two new nodes and divide the current node-index over
   ;; these two new nodes.
   (let* ((split-pos (floor (btree-node-index-count node) 2))
          (left (make-instance (btree-node-class btree)
-                              :parent parent
                               :btree btree
                               :leaf-p (btree-node-leaf-p node)))
          (right (make-instance (btree-node-class btree)
-                               :parent parent
                                :btree btree
                                :leaf-p (btree-node-leaf-p node))))
     ;; Divide the node over the two new nodes.
     (p-replace (btree-node-index left) (btree-node-index node)
-                    :end2 split-pos)
+               :end2 split-pos)
     (p-replace (btree-node-index right) (btree-node-index node)
-                    :start2 split-pos)
+               :start2 split-pos)
     (setf (btree-node-index-count left) split-pos
           (btree-node-index-count right) (- (btree-node-index-count node) split-pos))
     ;;
-    (let* ((node-pos (and parent (node-position node parent)))
-           (parent-binding (and parent (node-binding parent node-pos)))
-           (left-key
-            ;; The key that splits the two new nodes.
-            (smallest-key right))
-           (right-key
-            (if (null parent)
-                'key-irrelevant
-              (binding-key parent-binding))))
-      (if (p-eql node (btree-root btree))
-          ;; Make a new root.
-          (setf (btree-root btree) (make-root btree left-key left right-key right))
-        ;; Replace the original subnode by the left-child and
-        ;; add a new-binding with new-key & right-child.
-        (progn
-          (setf (binding-key parent-binding) left-key
-                (binding-value parent-binding) left)
-          ;; Insert a new binding for the right node.
-          (insert-new-binding parent (1+ node-pos) right-key right))))))
-
+    (let ((left-key
+           ;; The key that splits the two new nodes.
+           (biggest-key left)))
+      (cond ((p-eql node (btree-root btree))
+             ;; Make a new root.
+             (setf (btree-root btree)
+                   (make-root btree left-key left 'key-irrelevant right)))
+            (t
+             (let* ((parent (first parent-stack))
+                    (node-pos (node-position node parent))
+                    (parent-binding (node-binding parent node-pos))
+                    (old-key (binding-key parent-binding)))
+               (when (node-full-p btree parent)
+                 (setq parent (split-btree-node btree parent (rest parent-stack) old-key)
+                       node-pos (node-position node parent)))
+               ;; Replace the original subnode by the left-child and
+               ;; add a new-binding with new-key & right-child.
+               (setf (binding-key parent-binding) left-key
+                     (binding-value parent-binding) left)
+               ;; Insert a new binding for the right node.
+               (insert-new-binding parent (1+ node-pos) old-key right))))
+      ;; Return the node that's relevant for KEY
+      (if (or (eq key 'key-irrelevant)
+              (funcall (btree-key< btree) left-key key))
+          right
+        left))))
 
-(defun parent-binding (node parent)
-  (node-binding parent (node-position node parent)))
 
 (defun node-position (node parent)
   (p-position node (btree-node-index parent)
@@ -405,33 +456,40 @@
 
 
 (defun insert-new-binding (node position key value)
+  ;; This function must only be called if we know that the index isn't
+  ;; full already
   (unless (>= position (btree-node-index-count node))
     ;; Make room by moving bindings to the right.
     (let ((node-index (btree-node-index node))
           (length (btree-node-index-count node)))
       (p-replace node-index node-index
-                      :start1 (1+ position) :end1 (1+ length)
-                      :start2 position :end2 length)))
+                 :start1 (1+ position) :end1 (1+ length)
+                 :start2 position :end2 length)))
   ;; Insert new binding.
   (setf (node-binding node position) (make-binding key value))
   (incf (btree-node-index-count node)))
 
 
+;;
+;; Debugging
+;;
+
 (defun check-node (btree node)
   (loop for i below (1- (btree-node-index-count node))
         for left-key = (binding-key (node-binding node i))
         for right-key = (binding-key (node-binding node (1+ i)))
         do (unless (or (eql right-key 'key-irrelevant)
                        (funcall (btree-key< btree) left-key right-key))
+             (display-node node)
              (error "Inconsistent node ~S" node))))
 
 
+;;
+;; Leaf insert
+;;
 
-(defun leaf-insert (btree leaf key value if-exists)
-  (let ((binding (p-find key (btree-node-index leaf)
-                              :key #'binding-key
-                              :test (btree-key= btree)
-                              :end (btree-node-index-count leaf))))
+(defun leaf-insert (btree leaf parent-stack key value if-exists)
+  (let ((binding (find-binding-in-node key leaf btree)))
     (if binding
         ;; Key already exists.
         (if (btree-unique-keys-p btree)
@@ -452,21 +510,169 @@
           (unless (p-find value (binding-value binding) :test (btree-value= btree))
             (setf (binding-value binding)
                   (p-cons value (binding-value binding)))))
-      ;; The key doesn't exist yet. Create a new binding and add it to the
-      ;; leaf index in the right position.
-      (let ((new-position (p-position key (btree-node-index leaf)
-                                           :test (btree-key< btree)
-                                           :key #'binding-key
-                                           :end (btree-node-index-count leaf))))
-        (insert-new-binding leaf
-                            (or new-position (btree-node-index-count leaf))
-                            key
-                            (make-leaf-value btree value))))))
-
-
-(defun node-almost-full-p (btree node)
-  (>= (btree-node-index-count node) (1- (btree-max-node-size btree))))
+       ;; The key doesn't exist yet. Create a new binding and add it to the
+       ;; leaf index in the right position.
+       (progn
+        (when (node-full-p btree leaf)

[162 lines skipped]
--- /project/rucksack/cvsroot/rucksack/package.lisp	2006/05/18 12:46:57	1.3
+++ /project/rucksack/cvsroot/rucksack/package.lisp	2006/08/04 22:04:43	1.4
@@ -1,4 +1,4 @@
-;; $Id: package.lisp,v 1.3 2006/05/18 12:46:57 alemmens Exp $
+;; $Id: package.lisp,v 1.4 2006/08/04 22:04:43 alemmens Exp $
 
 #-(or allegro lispworks sbcl openmcl)
   (error "Unsupported implementation: ~A" (lisp-implementation-type))
@@ -78,12 +78,13 @@
 
    ;; Btrees
    #:btree
-   #:btree-key< #:btree-key= #:btree-value=
+   #:btree-key< #:btree-key<= #:btree-key= #:btree-key>= #:btree-key>
+   #:btree-value=
    #:btree-max-node-size #:btree-unique-keys-p
    #:btree-key-type #:btree-value-type
    #:btree-node-class #:btree-node
    ;; Functions
-   #:btree-search #:btree-insert #:map-btree
+   #:btree-search #:btree-insert #:btree-delete #:map-btree
    ;; Conditions
    #:btree-error #:btree-search-error #:btree-insertion-error
    #:btree-key-already-present-error #:btree-type-error
--- /project/rucksack/cvsroot/rucksack/test.lisp	2006/05/25 13:01:38	1.4
+++ /project/rucksack/cvsroot/rucksack/test.lisp	2006/08/04 22:04:43	1.5
@@ -1,4 +1,4 @@
-;; $Id: test.lisp,v 1.4 2006/05/25 13:01:38 alemmens Exp $
+;; $Id: test.lisp,v 1.5 2006/08/04 22:04:43 alemmens Exp $
 
 (in-package :test-rucksack)
 
@@ -153,29 +153,102 @@
 ;; Test btrees as just another persistent data structure.
 ;;
 
-(defun test-btree-insert (&key (n 20000) (node-size 100))
-  ;; Create a rucksack with btree that maps random integers to the
-  ;; equivalent strings in Roman notation.
-  (with-rucksack (rucksack *test-suite* :if-exists :supersede)
-    (with-transaction ()
-      (let ((btree (make-instance 'btree :value= 'string-equal
-                                  :max-node-size node-size)))
-        (loop for i from 1 to n
-              for key = (random n) do
-              (when (zerop (mod i 1000))
-                (format t "~D " i))
-              (btree-insert btree key (format nil "~R" key)))
-        (add-rucksack-root btree rucksack)))))
-
-(defun test-btree-dummy-insert (&key (n 20000))
-  ;; This function can be used for timing: subtract the time taken
-  ;; by this function from the time taken by TEST-BTREE-INSERT to
-  ;; get an estimate of the time needed to manipulate the btrees.
-  (loop for i from 1 to n
-        for key = (random n)
-        when (zerop (mod i 1000)) do (format t "~D " i)
-        collect (cons key (format nil "~R" key)))
-  t)
+(defun shuffle (array)
+  (loop with n = (array-dimension array 0)
+        repeat n
+        for i = (random n)
+        for j = (random n)
+        when (/= i j)
+        do (rotatef (aref array i) (aref array j))))
+
+(defun check-size (btree expected)
+  (format t "~&Counting~%")
+  (let ((count 0))
+    (map-btree btree
+               (lambda (key value)
+                 (declare (ignore key value))
+                 (incf count)))
+    (unless (= count expected)
+      (error "Wrong btree size - expected ~A, got ~A."
+             expected count))))
+
+(defun check-order (btree)
+  (format t "~&Checking order~%")
+  (rs::check-btree btree))
+
+(defun check-contents (btree)
+  (format t "~&Checking contents~%")
+  (map-btree btree
+             (lambda (key value)
+               (unless (string= value (format nil "~R" key))
+                 (error "Value mismatch: Expected ~S, got ~S."
+                        (format nil "~R" key) value)))))
+
+(defmacro with-transaction* ((&rest args) &body body)
+  `(with-transaction ,args
+     (prog1 (progn , at body)
+       (format t "~&Committing..."))))
+
+(defun test-btree (&key (n 20000) (node-size 100) (delete (floor n 10)) check-contents)
+  ;; Create a rucksack with a btree of size N that maps random
+  ;; integers to the equivalent strings as a cardinal English number.
+  ;; Use node size NODE-SIZE for the btree.
+  ;; If DELETE is not NIL, delete and reinsert that number of elements
+  ;; as well.
+  (let ((array (make-array n :initial-contents (loop for i below n collect i))))
+    (shuffle array)
+    (with-rucksack (rucksack *test-suite* :if-exists :supersede)
+      (with-transaction* ()
+        (format t "~&Inserting~%")
+        (let ((btree (make-instance 'btree :value= 'string-equal
+                                    :max-node-size node-size)))
+          (loop for key across array
+                for i from 1
+                when (zerop (mod i 1000))
+                do (format t "~D " i)
+                do (btree-insert btree key (format nil "~R" key)))
+          (add-rucksack-root btree rucksack))))
+    (with-rucksack (rucksack *test-suite*)
+      (with-transaction ()
+        (let ((btree (first (rucksack-roots rucksack))))
+          (check-order btree)
+          (check-size btree n)
+          (when check-contents
+            (check-contents btree))))
+      (when delete
+        (shuffle array)
+        (setq array (subseq array 0 delete))
+        (shuffle array)
+        (with-transaction* ()
+          (format t "~&Deleting~%")
+          (let ((btree (first (rucksack-roots rucksack))))
+            (dotimes (i delete)
+              (when (zerop (mod (1+ i) 1000))
+                (format t "~D " (1+ i)))
+              (btree-delete btree (aref array i)))
+            (check-order btree)
+            (check-contents btree)))
+        (with-transaction* ()
+          (let ((btree (first (rucksack-roots rucksack))))
+            (check-order btree)
+            (check-size btree (- n delete))
+            (when check-contents
+              (check-contents btree))
+            (format t "~&Reinserting~%")
+            (shuffle array)
+            (dotimes (i delete)
+              (when (zerop (mod (1+ i) 1000))
+                (format t "~D " (1+ i)))
+              (let ((key (aref array i)))
+                (btree-insert btree key (format nil "~R" key))))))
+        (with-transaction ()
+          (let ((btree (first (rucksack-roots rucksack))))
+            (check-order btree)
+            (check-size btree n)
+            (when check-contents
+              (check-contents btree)))))))
+  :ok)
+
 
 
 (defun test-btree-map (&key (display t))
@@ -187,3 +260,34 @@
                    (lambda (key value)
                      (when display
                        (format t "~&~D -> ~A~%" key value))))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Garbage collector
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun check-gc (n)
+  (with-rucksack (rucksack *test-suite* :if-exists :supersede)
+    (with-transaction ()
+      ;; after this, INNER can be reached directly from the root
+      (let* ((inner (p-cons "Waldorf" "Statler"))
+             (root (p-cons 42 inner)))
+        (add-rucksack-root root rucksack)))
+    (with-transaction ()
+      (let* ((root (first (rucksack-roots rucksack)))
+             (inner (p-cdr root))
+             (array (p-make-array n)))
+        ;; after this, INNER can't be reached from the root anymore
+        (setf (p-cdr root) 43)
+        ;; now let the GC do some work
+        (dotimes (i n)
+          (let ((string (format nil "~R" i)))
+            (setf (p-aref array i) (p-cons string string))))
+        ;; hook INNER back to the root again before we finish the
+        ;; transaction
+        (setf (p-car root) array
+              (p-cdr root) (p-cons 'bar (p-cons 'foo inner)))))
+    (with-transaction ()
+      (let* ((root (first (rucksack-roots rucksack)))
+             (inner (p-cdr (p-cdr (p-cdr root)))))
+        ;; we expect the list ("Waldorf" "Statler") here
+        (list (p-car inner) (p-cdr inner))))))




More information about the rucksack-cvs mailing list