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