]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
Multiple readers and variable optimization
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / CacheEntryBase.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.impl.query;
13
14 import java.util.ArrayList;
15 import java.util.Iterator;
16
17 import org.simantics.db.RelationInfo;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.db.impl.DebugPolicy;
20 import org.simantics.db.impl.graph.ReadGraphImpl;
21 import org.simantics.db.impl.procedure.InternalProcedure;
22 import org.simantics.db.impl.query.QueryProcessor.Runner2Procedure;
23 import org.simantics.db.procedure.ListenerBase;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 abstract public class CacheEntryBase<Procedure> extends CacheEntry<Procedure> {
28
29     private static final Logger LOGGER = LoggerFactory.getLogger(CacheEntryBase.class);
30     
31         // Default level is something that is not quite a prospect but still allows for ordering within CacheCollectionResult 
32         public static final short UNDEFINED_LEVEL = 5;
33         
34         public short level = UNDEFINED_LEVEL;
35         public short age = 0;
36         public int GCStatus = 0;
37         
38     final public static CacheEntryBase[] NONE = new CacheEntryBase[0];
39
40         static Object NO_RESULT = new Object();
41         static protected Object INVALID_RESULT = new Object();
42         
43 //      // Just created
44 //    static protected Object FRESH = new Object() { public String toString() { return "CREATED"; }};
45     // Result is computed - no exception
46     static protected Object READY = new Object() { public String toString() { return "READY"; }};
47     // Computation is under way
48     static protected Object PENDING = new Object() { public String toString() { return "PENDING"; }};
49     // Entry is discarded and is waiting for garbage collect
50     static protected Object DISCARDED = new Object() { public String toString() { return "DISCARDED"; }};
51     // The result has been invalidated
52     static protected Object REQUIRES_COMPUTATION = new Object() { public String toString() { return "REFUTED"; }};
53     // The computation has excepted - the exception is in the result
54     static protected Object EXCEPTED = new Object() { public String toString() { return "EXCEPTED"; }};
55
56     // This indicates the status of the entry
57     public Object statusOrException = REQUIRES_COMPUTATION;
58     
59     private CacheEntry p1 = null;
60     private Object p2OrParents = null;
61     
62     private int hash = 0;
63     
64     @Override
65     final public int hashCode() {
66         if(hash == 0) hash = makeHash();
67         return hash;
68     }
69     
70     abstract int makeHash();
71     
72     // This can be tested to see if the result is finished
73     private Object result = NO_RESULT;
74     
75     final public boolean isFresh() {
76         return REQUIRES_COMPUTATION == statusOrException;
77     }
78
79     public void setReady() {
80         statusOrException = READY;
81     }
82
83     @Deprecated
84     final public boolean isReady() {
85         return READY == statusOrException || EXCEPTED == statusOrException;
86     }
87     
88     @Override
89     public void discard() {
90         if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: discarded " + this);
91         statusOrException = DISCARDED;
92     }
93     
94     @Override
95     final public boolean isDiscarded() {
96         return DISCARDED == statusOrException;
97     }
98     
99     @Override
100     final public void refute() {
101         if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: refuted " + this);
102         statusOrException = REQUIRES_COMPUTATION;
103     }
104     
105     @Override
106     final public boolean isRefuted() {
107         return REQUIRES_COMPUTATION == statusOrException;
108     }
109
110     @Override
111     final public void except(Throwable t) {
112         if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: excepted " + this);
113         if(statusOrException != DISCARDED) {
114                 statusOrException = EXCEPTED;
115                 result = t;
116         } else {
117                 LOGGER.warn("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), t);
118                 result = t;
119         }
120     }
121     
122     final public void checkAndThrow() throws DatabaseException {
123         if(isExcepted()) {
124             Throwable throwable = (Throwable)result;
125             if(throwable instanceof DatabaseException) throw (DatabaseException)throwable;
126             else throw new DatabaseException(throwable);
127         }
128     }
129     
130     @Override
131     final public boolean isExcepted() {
132         return EXCEPTED == statusOrException;
133     }
134
135     @Override
136     final public void setPending() {
137         statusOrException = PENDING;
138     }
139     
140     @Override
141     final public boolean isPending() {
142         return PENDING == statusOrException;
143     }
144     
145     final public boolean requiresComputation() {
146         return REQUIRES_COMPUTATION == statusOrException;
147     }
148     
149     final public boolean assertPending() {
150         boolean result = isPending();
151         if(!result) {
152                 LOGGER.warn("Assertion failed, expected pending, got " + statusOrException);
153         }
154         return result;
155     }
156
157     final public boolean assertNotPending() {
158         boolean result = !isPending();
159         if(!result) {
160                 new Exception(this +  ": Assertion failed, expected not pending, got " + statusOrException).printStackTrace();
161         }
162         return result;
163     }
164
165     final public boolean assertNotDiscarded() {
166         boolean result = !isDiscarded();
167         if(!result) {
168                 new Exception(this +  ": Assertion failed, expected not discarded, got " + statusOrException).printStackTrace();
169         }
170         return result;
171     }
172
173     @Override
174     public void setResult(Object result) {
175         this.result = result;
176     }
177     
178     @SuppressWarnings("unchecked")
179     @Override
180     final public <T> T getResult() {
181         assert(statusOrException != DISCARDED);
182         return (T)result;
183     }
184     
185     @Override
186     public void clearResult(QuerySupport support) {
187         setResult(NO_RESULT);
188     }
189     
190     @Override
191     final public void addParent(CacheEntry entry) {
192          
193         assert(entry != null);
194         
195         if(p1 == entry) {
196                 return;
197         }
198         if(p2OrParents == entry) {
199                 return;
200         }
201         if(p1 == null) {
202                 p1 = entry;
203         } else if(p2OrParents == null) {
204                 p2OrParents = entry;
205         } else if(p2OrParents instanceof QueryIdentityHashSet) {
206             ((QueryIdentityHashSet)p2OrParents).add(entry);
207             ((QueryIdentityHashSet)p2OrParents).purge();
208         } else {
209             CacheEntry tmp = (CacheEntry)p2OrParents;
210             p2OrParents = new QueryIdentityHashSet(2);
211             ((QueryIdentityHashSet)p2OrParents).add(tmp);
212             ((QueryIdentityHashSet)p2OrParents).add(entry);
213         }
214         
215     }
216
217     @Override
218     CacheEntry pruneFirstParents() {
219
220         if(p1 == null) {
221                 // No parents
222                 return null;
223         }
224         
225         if(!p1.isDiscarded()) {
226                 
227                 // First parent is still active
228                 return p1;
229                 
230         } else {
231                 
232                 // Clear p1
233                 p1 = null;
234                 
235                 // First parent is discarded => look for more parents
236                 if(p2OrParents instanceof QueryIdentityHashSet) {
237
238                         QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
239                         CacheEntry entry = set.removeDiscarded();
240                         if(entry == null) p2OrParents = null;
241                         p1 = entry;
242                         return p1;
243
244                 } else if(p2OrParents instanceof CacheEntry) {
245                         
246                         CacheEntry entry = (CacheEntry)p2OrParents;
247                         if(entry.isDiscarded()) {
248                                 // Second entry is also discarded => all empty
249                                 p2OrParents = null;
250                                 return null;
251                         } else {
252                                 p1 = entry;
253                                 p2OrParents = null;
254                                 return p1;
255                         }
256                         
257                 } else {
258                 
259                         // Nothing left
260                         return null;
261                         
262                 }
263                 
264         }
265         
266     }
267     
268     @Override
269     final public void removeParent(CacheEntry entry) {
270        
271         if(p1 == null) {
272             if(p2OrParents != null) throw new Error("CacheEntryBase.removeParent: corrupted parents (p1 == null, while p2OrParents != null).");
273             else throw new Error("CacheEntryBase.removeParent: no parents.");
274         }
275         if(p1 == entry) {
276             if(p2OrParents == null) {
277                 p1 = null;
278             } else if(p2OrParents instanceof QueryIdentityHashSet) {
279                 QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
280                 int size = set.size();
281                 if(size == 0) {
282                     p1 = null;
283                     p2OrParents = null;
284                 } else if (size == 1) {
285                     CacheEntry next = set.iterator().next();
286                     p1 = next;
287                     set = null;
288                 } else if(set.size() == 2) {
289                     Iterator<CacheEntry> iterator = set.iterator();
290                     p1 = iterator.next();
291                     p2OrParents = iterator.next();
292                 } else {
293                     p1 = set.iterator().next();
294                     set.remove(p1);
295                 }
296             } else {
297                 p1 = (CacheEntry)p2OrParents;
298                 p2OrParents = null;
299             }
300             
301         } else if(p2OrParents.getClass() == QueryIdentityHashSet.class) {
302             
303             QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
304             boolean success = set.remove(entry);
305             if(!success) {
306                 throw new Error("CacheEntryBase.removeParent: parent was not found.");
307             }
308             assert(set.size() >= 1);
309             if(set.size() == 1) {
310                 p2OrParents = set.iterator().next();
311             }
312             
313         } else {
314             if(p2OrParents == entry) {
315                 p2OrParents = null;
316             } else {
317                 throw new Error("CacheEntryBase.removeParent: had 2 parents but neither was removed.");
318             }
319         }
320     }
321
322     @Override
323     final public boolean hasParents() {
324         assert(statusOrException != DISCARDED);
325         return p1 != null;
326     }
327     
328     @Override
329         final public Iterable<CacheEntry<?>> getParents(QueryProcessor processor) {
330
331                 ArrayList<CacheEntry<?>> result = new ArrayList<CacheEntry<?>>();
332                 if(p1 != null) result.add(p1);
333                 if(p2OrParents != null) {
334                 if(p2OrParents instanceof QueryIdentityHashSet) {
335                         for(CacheEntry entry : (QueryIdentityHashSet)p2OrParents) {
336                                 result.add(entry);
337                         }
338                 } else {
339                         result.add((CacheEntry)p2OrParents);
340                 }
341                 }
342                 fillImpliedParents(processor, result);
343                 return result;
344                 
345         }
346     
347     @Override
348     CacheEntry getFirstParent(QueryProcessor processor) {
349         return p1;
350     }
351     
352     @Override
353     boolean moreThanOneParent(QueryProcessor processor) {
354         return p2OrParents != null;
355     }
356     
357     @Override
358     int parentCount(QueryProcessor processor) {
359         if(p2OrParents != null) {
360                 if(p2OrParents instanceof QueryIdentityHashSet) {
361                         return ((QueryIdentityHashSet)p2OrParents).size()+1;
362                 } else {
363                         return 2;
364                 }
365         } else {
366                 return p1 != null ? 1 : 0;
367         }
368         
369     }
370     
371     protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry<?>> result) {
372     }
373     
374     protected String internalError() {
375         return toString() + " " + statusOrException + " " + result;
376     }
377
378     
379     protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) throws DatabaseException {
380         if(isExcepted()) {
381                 procedure.exception(graph, (Throwable)getResult());
382                 return true;
383         } else {
384                 return false;
385         }
386     }
387     
388     protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) throws DatabaseException {
389         if(isExcepted()) {
390                 procedure.exception(graph, (Throwable)getResult());
391                 return true;
392         } else {
393                 return false;
394         }
395     }
396
397     protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) throws DatabaseException {
398         if(isExcepted()) {
399                 procedure.exception(graph, (Throwable)getResult());
400                 return true;
401         } else {
402                 return false;
403         }
404     }
405     
406     @Override
407     boolean isImmutable(ReadGraphImpl graph) throws DatabaseException {
408         return false;
409     }
410     
411     @Override
412     boolean shouldBeCollected() {
413         return true;
414     }
415     
416     @Override
417     short getLevel() {
418         return level;
419     }
420     
421     @Override
422     short setLevel(short level) {
423         short existing = this.level;
424         this.level = level;
425         return existing;
426     }
427     
428     @Override
429     void prepareRecompute(QuerySupport querySupport) {
430                 setPending();
431                 clearResult(querySupport);
432     }
433     
434     @Override
435     int getGCStatus() {
436         return GCStatus;
437     }
438     
439     @Override
440     int setGCStatus(int status) {
441         GCStatus = status;
442         return GCStatus;
443     }
444     
445     @Override
446     void setGCStatusFlag(int flag, boolean value) {
447         if(value) {
448                 GCStatus |= flag;
449         } else {
450                 GCStatus &= ~flag;
451         }
452     }
453     
454     @Override
455     public Object getOriginalRequest() {
456         // This is the original request for all built-in queries
457         return getQuery();
458     }
459
460         public CacheEntryBase() {
461         }
462         
463     abstract public Object compute(ReadGraphImpl graph, Procedure procedure) throws DatabaseException;
464     
465 }