root/trunk/bknr/datastore/src/indices/indexed-class.lisp

Revision 3942, 16.6 kB (checked in by hans, 2 months ago)

Merge from anon-transaction-fixes-2 branch. This changeset removes
make-object and initialize-persistent-instance, makes the allocation
of object IDs simpler and more safe and removes several relicts from
previous refactoring iterations. Also, the store tests have been
extended significantly to test pathological cases and create objects
from multiple threads.

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 (in-package :bknr.indices)
2
3 ;; XXX slots from inherited class indices
4
5 ;;; XXX update-instance-for-different-class
6 ;;; XXX update-instance-for-redefined-class
7 ;;; XXX index-object als toplevel class einfuehren
8 ;;; XXX existierende objekte in die indexe eintrage (geht nicht :( )
9 ;;; ...
10 ;;; restarts mal richtig machen
11
12 (defclass indexed-class (standard-class)
13   ((indices :initarg :indices :initform nil
14             :accessor indexed-class-indices)
15    (old-indices :initarg :old-indices :initform nil
16                 :accessor indexed-class-old-indices)
17    (index-definitions :initarg :class-indices :initform nil
18                       :accessor indexed-class-index-definitions)))
19
20 (defstruct index-holder
21   class slots name index index-subclasses)
22
23 (defmethod indexed-class-index-named ((class indexed-class) index-name)
24   (let ((index-holder (find index-name (indexed-class-indices class)
25                             :key #'index-holder-name)))
26     (when index-holder
27       (index-holder-index index-holder))))
28
29 (defmethod validate-superclass ((sub indexed-class) (super standard-class))
30   t)
31
32 (defclass index-direct-slot-definition (standard-direct-slot-definition)
33   ((index :initarg :index :initform nil
34           :reader index-direct-slot-definition-index
35           :documentation "Slot keyword for an already existing index")
36
37    (index-var :initarg :index-var :initform nil
38               :reader index-direct-slot-definition-index-var
39               :documentation "Symbol that will be bound to the index")
40    
41    (index-type :initarg :index-type :initform nil
42                :reader index-direct-slot-definition-index-type
43                :documentation "Slot keyword to specify the class of a new slot index")
44    (index-initargs :initarg :index-initargs :initform nil
45                :reader index-direct-slot-definition-index-initargs
46                :documentation "Arguments that will be passed to
47 INDEX-CREATE when creating a new slot index")
48
49    (index-reader :initform nil
50                  :initarg :index-reader
51                  :accessor index-direct-slot-definition-index-reader
52                  :documentation "Name of a function that will be created to query the slot index")
53    (index-values :initform nil
54                  :initarg :index-values
55                  :accessor index-direct-slot-definition-index-values
56                  :documentation "Name of a function that will be
57 created to get the values stored in the index")
58    (index-mapvalues :initform nil
59                     :initarg :index-mapvalues
60                     :accessor index-direct-slot-definition-index-mapvalues
61                     :documentation "Name of a function that will be
62 created to map over the values stored in the index")
63    (index-keys :initform nil
64                :initarg :index-keys
65                :accessor index-direct-slot-definition-index-keys
66                :documentation "Name of a function that will be created
67 to get the keys stored in the index")
68
69    (index-subclasses :initarg :index-subclasses :initform t
70                      :accessor index-direct-slot-definition-index-subclasses
71                      :documentation "Specify if the slot index will
72 also index subclasses of the class to which the slot belongs, default is T")
73    
74    (class :initform nil
75           :accessor index-direct-slot-definition-class)))
76
77 (defclass index-effective-slot-definition (standard-effective-slot-definition)
78   ((indices :initarg :indices :initform nil
79             :accessor index-effective-slot-definition-indices)))
80
81 (defmethod class-all-indexed-superclasses ((class indexed-class))
82   (let (result)
83     (labels ((superclasses (class)
84                (let ((classes (remove-if-not #'(lambda (class)
85                                                  (typep class 'indexed-class))
86                                              (class-direct-superclasses class))))
87                  (dolist (class classes)
88                    (unless (class-finalized-p class)
89                      (finalize-inheritance class))
90                    (push class result)
91                    (superclasses class)))))
92       (superclasses class))
93     (nreverse result)))
94
95 (defmethod direct-slot-definition-class ((class indexed-class) &key index index-type)
96   (if (or index index-type)
97       'index-direct-slot-definition
98       (call-next-method)))
99
100 (defmethod effective-slot-definition-class ((class indexed-class) &rest initargs)
101   (declare (ignore initargs))
102   'index-effective-slot-definition)
103
104 (defun defun-and-compile (defun)
105   (let ((function (second defun)))
106     (when function
107       (eval defun)
108       (compile function))))
109
110 (defun create-index-access-functions (index &key index-reader index-values
111                                       index-mapvalues index-keys index-var)
112   (defun-and-compile
113       `(defun ,index-reader (key) (index-get ,index key)))
114   (defun-and-compile
115       `(defun ,index-values ()
116         (index-values ,index)))
117   (defun-and-compile
118       `(defun ,index-mapvalues (fun)
119         (index-mapvalues ,index fun)))
120   (defun-and-compile
121       `(defun ,index-keys ()
122         (index-keys ,index)))
123   (when index-var
124     (when (boundp index-var)
125       (warn "~A is already bound to ~A, rebinding to ~A"
126             index-var (eval index-var) index))
127     (eval `(defparameter ,index-var ,index))))
128
129 (defun make-index-object (&key index type initargs reader values mapvalues slots keys var)
130   (let ((index-object (if index
131                           (eval index)
132                           (apply #'index-create
133                                  (append (cons type (eval-initargs initargs))
134                                          (list :slots slots))))))
135     (when index-object
136       (create-index-access-functions index-object :index-reader reader
137                                      :index-values values
138                                      :index-mapvalues mapvalues
139                                      :index-keys keys
140                                      :index-var var))
141     index-object))
142
143 (defmethod compute-effective-slot-definition :around ((class indexed-class)
144                                                       name direct-slots)
145   (declare (ignore name))
146   (let* ((normal-slot (call-next-method))
147          (direct-slots (remove-if-not #'(lambda (slot)
148                                           (typep slot 'index-direct-slot-definition))
149                                       direct-slots))
150          (direct-slot (first direct-slots)))
151     (when (and (typep normal-slot 'index-effective-slot-definition)
152                direct-slot
153                (or (not (index-direct-slot-definition-class direct-slot))
154                    (eql (index-direct-slot-definition-class direct-slot) class)))
155       (setf (index-direct-slot-definition-class direct-slot) class)
156       (with-slots (index index-type index-initargs index-subclasses index-keys
157                          index-reader index-values index-mapvalues index-var) direct-slot
158         (when (or index index-type)
159           (let* ((name (slot-definition-name direct-slot))
160                  (index-object (make-index-object :index index
161                                                   :type index-type
162                                                   :initargs index-initargs
163                                                   :reader index-reader
164                                                   :keys index-keys
165                                                   :values index-values
166                                                   :mapvalues index-mapvalues
167                                                   :var index-var
168                                                   :slots (list name))))
169             (when index-object
170               (push (make-index-holder :class class :slots (list name)
171                                        :name name :index index-object
172                                        :index-subclasses index-subclasses)
173                     (indexed-class-indices class)))))))
174     normal-slot))
175
176 (defmethod compute-class-indices ((class indexed-class) class-indices)
177   (unless (class-finalized-p class)
178     (finalize-inheritance class))
179   (let* ((class-slots (class-slots class))
180          (slot-names (mapcar #'slot-definition-name class-slots)))
181
182     ;;; create new class indices
183     (dolist (class-index class-indices)
184       #+nil
185       (format t "class-index ~A~%" class-index)
186      
187       (destructuring-bind (name &key index-reader index-values index-mapvalues
188                                 index-keys (index-subclasses t) index-initargs
189                                 (slots :all-slots) index-type
190                                 index) class-index
191         (when (eql slots :all-slots)
192           (setf slots slot-names))
193
194         (let ((index-object (make-index-object :index index
195                                                :type index-type
196                                                :initargs index-initargs
197                                                :reader index-reader
198                                                :values index-values
199                                                :keys index-keys
200                                                :mapvalues index-mapvalues
201                                                :slots slots)))
202           (when index-object
203             (push (make-index-holder :class class :slots slots
204                                      :name name :index index-object
205                                      :index-subclasses index-subclasses)
206                   (indexed-class-indices class))))))
207
208     #+nil
209     (format t "superclasses ~A~%" (class-all-indexed-superclasses class))
210    
211     ;;; class indices from superclasses
212     (dolist (superclass (class-all-indexed-superclasses class))
213       (setf (indexed-class-indices class)
214             (remove-duplicates
215              (append (indexed-class-indices class)
216                      (remove nil (indexed-class-indices superclass)
217                              :key #'index-holder-index-subclasses))
218              :key #'index-holder-index)))
219
220     (dolist (holder (indexed-class-indices class))
221       (dolist (slot-name (index-holder-slots holder))
222         (let ((slot (find slot-name class-slots :key #'slot-definition-name)))
223           #+nil
224           (format t "slot ~A indx ~A~%" slot holder)
225           (unless (and slot
226                        (typep slot 'index-effective-slot-definition ))
227             (error "Could not find slot ~A to store index ~A~%" slot-name holder))
228           (pushnew (index-holder-index holder)
229                    (index-effective-slot-definition-indices slot)))))))
230
231 #+allegro
232 (defmethod finalize-inheritance :after ((class indexed-class))
233   (compute-class-indices class (indexed-class-index-definitions class))
234   (reinitialize-class-indices class))
235
236 (defun validate-index-declaration (class indices)
237   (dolist (index indices)
238     (when (and (getf (cdr index) :index)
239                (getf (cdr index) :index-type))
240       (error "Can't have both :INDEX and :INDEX-TYPE in index ~A of ~A" (car index) class))))
241
242 (defmethod initialize-instance :before ((class indexed-class) &key class-indices)
243   (validate-index-declaration class class-indices))
244
245 (defmethod reinitialize-instance :before ((class indexed-class) &key class-indices)
246   (validate-index-declaration class class-indices))
247
248 ;;; avoid late instantiation
249
250 #+(or allegro cmu openmcl sbcl)
251 (defmethod initialize-instance :after ((class indexed-class) &key)
252   (compute-class-indices class (indexed-class-index-definitions class))
253   (reinitialize-class-indices class))
254
255 #+(or allegro cmu openmcl sbcl)
256 (defmethod reinitialize-instance :after ((class indexed-class) &key)
257   (compute-class-indices class (indexed-class-index-definitions class))
258   (reinitialize-class-indices class))
259
260 (defmethod reinitialize-class-indices ((class indexed-class))
261   (let ((old-indices (remove class (indexed-class-old-indices class)
262                              :test-not #'eql :key #'index-holder-class))
263         (indices (remove class (indexed-class-indices class)
264                          :test-not #'eql :key #'index-holder-class)))
265     (when old-indices
266       (dolist (holder indices)
267         (let ((old-holder (find (index-holder-name holder) old-indices
268                                 :key #'index-holder-name)))
269           (when old-holder
270             (index-reinitialize (index-holder-index holder)
271                                 (index-holder-index old-holder))))))))
272
273 (defmethod reinitialize-instance :before ((class indexed-class) &key)
274   (setf (indexed-class-old-indices class) (indexed-class-indices class)
275         (indexed-class-indices class) nil))
276
277 ;;; Hier koennen wir keine :AROUND method fuer COMPUTE-SLOTS bauen,
278 ;;; weil die LISP-Implementierung die Allocation von dem neuen
279 ;;; DESTROYED-P Slot bestimmen muss, und zwar auch im :AROUND. Das
280 ;;; koennen wir leider nicht uebernehmen.
281
282 (defmethod compute-slots ((class indexed-class))
283   (let* ((normal-slots (call-next-method))
284          (destroyed-p-slot #.`(make-instance
285                                'index-effective-slot-definition
286                                :name 'destroyed-p
287                                :initform nil
288                                :class class
289                                #+(or cmu sbcl)
290                                ,@'(:readers nil :writers nil)
291                                :initfunction #'(lambda () nil))))
292     (cons destroyed-p-slot normal-slots)))
293
294 (defvar *indexed-class-override* nil)
295
296 (defmethod slot-value-using-class :before ((class indexed-class) object slot)
297   (when (and (not (eql (slot-definition-name slot) 'destroyed-p))
298              (object-destroyed-p object)
299              (not *indexed-class-override*))
300     (error "Can not get slot ~A of destroyed object of class ~a."
301            (slot-definition-name slot) (class-name (class-of object)))))
302
303 (defmethod (setf slot-value-using-class) :before
304     (newvalue (class indexed-class) object slot)
305   (declare (ignore newvalue))
306   (when (and (not (eql (slot-definition-name slot) 'destroyed-p))
307              (object-destroyed-p object)
308              (not *indexed-class-override*))
309     (error "Can not set slot ~A of destroyed object ~a."
310            (slot-definition-name slot) (class-name (class-of object)))))
311
312 (defmethod slot-makunbound-using-class :before ((class indexed-class) object slot)
313   (when (and (not (eql (if (symbolp slot)
314                            slot
315                            (slot-definition-name slot))
316                        'destroyed-p))
317              (object-destroyed-p object)
318              (not *indexed-class-override*))
319     (error "Can not MAKUNBOUND slot ~A of destroyed object ~a."
320            (slot-definition-name slot) (class-name (class-of object)))))
321
322 (defvar *in-make-instance-p* nil)
323
324 (defvar *indices-remove-p* t)
325
326 (defmethod make-instance :around ((class indexed-class) &key)
327   (let* ((*in-make-instance-p* t)
328          (object (call-next-method))
329          (added-indices)
330          (error t))
331     (unwind-protect
332          (progn
333            (dolist (index (mapcar #'index-holder-index (indexed-class-indices class)))
334              (index-add index object)
335              (push index added-indices))
336            (setf error nil)
337            object)
338       (when error
339         (dolist (index added-indices)
340           (index-remove index object))))
341     object))
342
343 (defmethod (setf slot-value-using-class) :around
344     (newvalue (class indexed-class) object (slot index-effective-slot-definition))
345   (declare (ignore newvalue))
346
347   (when (eql (slot-definition-name slot) 'destroyed-p)
348     (return-from slot-value-using-class  (call-next-method)))
349
350   (when *in-make-instance-p*
351     (return-from slot-value-using-class (call-next-method)))
352  
353   (let* ((indices (index-effective-slot-definition-indices slot))
354          (slot-name (slot-definition-name slot))
355          (previous-slot-boundp (slot-boundp object slot-name))
356          (previous-slot-value (when previous-slot-boundp
357                                 (slot-value object slot-name))))
358
359     #+nil
360     (format t "indices ~A~%" indices)
361
362     (when (and previous-slot-boundp
363                *indices-remove-p*)
364       (let ((changed-indices)
365             (error t))
366         (unwind-protect
367              (progn
368                (dolist (index indices)
369                  (index-remove index object)
370                  (push index changed-indices))
371                (setf error nil))
372           (when error
373             (dolist (index changed-indices)
374               (index-add index object))))))
375    
376     (let ((result (call-next-method)))
377       #+nil
378       (format t "set slot ~A of ~a to ~A, value is ~a~%"
379               (slot-definition-name slot)
380               object newvalue
381               (slot-value object (slot-definition-name slot)))
382      
383       (when (slot-boundp object (slot-definition-name slot))
384         (let ((error t)
385               (changed-indices nil))
386           (unwind-protect
387                (progn
388                  (dolist (index indices)
389                    (index-add index object)
390                    (push index changed-indices))
391                  (setf error nil))
392             (when error
393               (dolist (index changed-indices)
394                 (index-remove index object))
395               (let ((*indices-remove-p* nil))
396                 (if previous-slot-boundp
397                     (setf (slot-value object slot-name) previous-slot-value)
398                     (slot-makunbound object slot-name)))))))
399       result)))
400
401 (defmethod slot-makunbound-using-class
402     ((class indexed-class) object (slot index-effective-slot-definition))
403   (let* ((slot-name (slot-definition-name slot))
404          (previous-slot-boundp (slot-boundp object slot-name))
405          (indices (index-effective-slot-definition-indices slot)))
406     (when (and previous-slot-boundp
407                *indices-remove-p*)
408       (let ((changed-indices nil)
409             (error t))
410         (unwind-protect
411              (progn
412                (dolist (index indices)
413                  (index-remove index object)
414                  (push index changed-indices))
415                (setf error nil))
416           (when error
417             (dolist (index changed-indices)
418               (index-add index object))))))
419     (call-next-method)))
420
421 (defmethod clear-class-indices ((class indexed-class))
422   (map nil #'(lambda (holder) (index-clear (index-holder-index holder)))
423        (indexed-class-indices class)))
424
425 (defmethod clear-slot-indices ((slot index-effective-slot-definition))
426   (map nil #'index-clear (index-effective-slot-definition-indices slot)))
427
428 (defmethod class-slot-indices ((class indexed-class) slot-name)
429   (index-effective-slot-definition-indices (find slot-name (class-slots class)
430                                                  :key #'slot-definition-name)))
431
432 (defmethod class-slot-index ((class indexed-class) slot-name)
433   (let ((holder (find-if #'(lambda (holder) (and (eql (index-holder-class holder) class)
434                                                  (eql (index-holder-name holder) slot-name)))
435                          (indexed-class-indices class))))
436     (when holder
437       (index-holder-index holder))))
438        
439 ;;; destroy object mechanic
440
441 (defgeneric destroy-object-with-class (class object))
442 (defgeneric destroy-object (object)
443   (:documentation "Destroy the given object, and delete it from the indices."))
444
445 (defmethod destroy-object-with-class ((class standard-class) object)
446   (declare (ignore object))
447   (error "Can not destroy an object that is not indexed."))
448
449 (defmethod destroy-object-with-class ((class indexed-class) object)
450   (dolist (index (mapcar #'index-holder-index (indexed-class-indices class)))
451     (index-remove index object))
452   (setf (slot-value object 'destroyed-p) t))
453
454 (defmethod destroy-object ((object t))
455   (destroy-object-with-class (class-of object) object))
456
457 (defmethod object-destroyed-p ((object t))
458   (and object
459        (slot-boundp object 'destroyed-p)
460        (slot-value object 'destroyed-p)))
461
Note: See TracBrowser for help on using the browser.