]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / CacheEntryBase.java
index 5776857d330ad98374d9b8c07d575ddb0118f7b2..98e7f0d00554f1c3f351339d8aa4b891028966ae 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * Copyright (c) 2007, 2018 Association for Decentralized Information Management
  * in Industry THTH ry.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
 package org.simantics.db.impl.query;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 
+import org.simantics.databoard.Bindings;
+import org.simantics.db.DevelopmentKeys;
 import org.simantics.db.exception.DatabaseException;
-import org.simantics.db.impl.DebugPolicy;
 import org.simantics.db.impl.graph.ReadGraphImpl;
 import org.simantics.db.impl.procedure.InternalProcedure;
+import org.simantics.utils.Development;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-abstract public class CacheEntryBase extends CacheEntry {
+public abstract class CacheEntryBase<Procedure> extends CacheEntry<Procedure> {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(CacheEntryBase.class);
     
@@ -34,11 +37,11 @@ abstract public class CacheEntryBase extends CacheEntry {
        
     final public static CacheEntryBase[] NONE = new CacheEntryBase[0];
 
-       static private Object NO_RESULT = new Object();
-       static protected Object INVALID_RESULT = new Object();
+       static Object NO_RESULT = new Object() { public String toString() { return "NO_RESULT"; }};
+       static protected Object INVALID_RESULT = new Object() { public String toString() { return "INVALID_RESULT"; }};
        
-       // Just created
-    static protected Object FRESH = new Object() { public String toString() { return "CREATED"; }};
+//     // Just created
+//    static protected Object FRESH = new Object() { public String toString() { return "CREATED"; }};
     // Result is computed - no exception
     static protected Object READY = new Object() { public String toString() { return "READY"; }};
     // Computation is under way
@@ -46,12 +49,12 @@ abstract public class CacheEntryBase extends CacheEntry {
     // Entry is discarded and is waiting for garbage collect
     static protected Object DISCARDED = new Object() { public String toString() { return "DISCARDED"; }};
     // The result has been invalidated
-    static protected Object REFUTED = new Object() { public String toString() { return "REFUTED"; }};
+    static protected Object REQUIRES_COMPUTATION = new Object() { public String toString() { return "REFUTED"; }};
     // The computation has excepted - the exception is in the result
     static protected Object EXCEPTED = new Object() { public String toString() { return "EXCEPTED"; }};
 
     // This indicates the status of the entry
-    public Object statusOrException = FRESH;
+    public Object statusOrException = REQUIRES_COMPUTATION;
     
     private CacheEntry p1 = null;
     private Object p2OrParents = null;
@@ -67,13 +70,14 @@ abstract public class CacheEntryBase extends CacheEntry {
     abstract int makeHash();
     
     // This can be tested to see if the result is finished
-    private Object result = NO_RESULT;
+    Object result = NO_RESULT;
     
     final public boolean isFresh() {
-       return FRESH == statusOrException;
+       return REQUIRES_COMPUTATION == statusOrException;
     }
 
     public void setReady() {
+        assert(result != NO_RESULT);
        statusOrException = READY;
     }
 
@@ -84,7 +88,11 @@ abstract public class CacheEntryBase extends CacheEntry {
     
     @Override
     public void discard() {
-       if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: discarded " + this);
+               if (Development.DEVELOPMENT) {
+                       if(Development.<Boolean>getProperty(DevelopmentKeys.CACHE_ENTRY_STATE, Bindings.BOOLEAN)) {
+                               System.err.println("[QUERY STATE]: discarded " + this);
+                       }
+               }
        statusOrException = DISCARDED;
     }
     
@@ -95,24 +103,32 @@ abstract public class CacheEntryBase extends CacheEntry {
     
     @Override
     final public void refute() {
-       if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: refuted " + this);
-       statusOrException = REFUTED;
+               if (Development.DEVELOPMENT) {
+                       if(Development.<Boolean>getProperty(DevelopmentKeys.CACHE_ENTRY_STATE, Bindings.BOOLEAN)) {
+                               System.err.println("[QUERY STATE]: refuted " + this);
+                       }
+               }
+       statusOrException = REQUIRES_COMPUTATION;
     }
     
     @Override
     final public boolean isRefuted() {
-        return REFUTED == statusOrException;
+        return REQUIRES_COMPUTATION == statusOrException;
     }
 
     @Override
-    final public void except(Throwable t) {
-       if(DebugPolicy.QUERY_STATE) System.out.println("[QUERY STATE]: excepted " + this);
+    public void except(Throwable throwable) {
+               if (Development.DEVELOPMENT) {
+                       if(Development.<Boolean>getProperty(DevelopmentKeys.CACHE_ENTRY_STATE, Bindings.BOOLEAN)) {
+                               System.err.println("[QUERY STATE]: excepted " + this);
+                       }
+               }
        if(statusOrException != DISCARDED) {
                statusOrException = EXCEPTED;
-               result = t;
+               result = throwable;
        } else {
-               LOGGER.warn("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), t);
-               result = t;
+               LOGGER.warn("Cache entry got excepted status after being discarded: " + getClass().getSimpleName(), throwable);
+               result = throwable;
        }
     }
     
@@ -130,8 +146,9 @@ abstract public class CacheEntryBase extends CacheEntry {
     }
 
     @Override
-    final public void setPending() {
-       statusOrException = PENDING;
+    public void setPending(QuerySupport querySupport) {
+        statusOrException = PENDING;
+        clearResult(querySupport);
     }
     
     @Override
@@ -139,6 +156,10 @@ abstract public class CacheEntryBase extends CacheEntry {
         return PENDING == statusOrException;
     }
     
+    final public boolean requiresComputation() {
+       return REQUIRES_COMPUTATION == statusOrException;
+    }
+    
     final public boolean assertPending() {
        boolean result = isPending();
        if(!result) {
@@ -168,6 +189,7 @@ abstract public class CacheEntryBase extends CacheEntry {
        this.result = result;
     }
     
+    @SuppressWarnings("unchecked")
     @Override
     final public <T> T getResult() {
         assert(statusOrException != DISCARDED);
@@ -256,7 +278,31 @@ abstract public class CacheEntryBase extends CacheEntry {
        }
         
     }
-    
+
+    @Override
+    void pruneParentSet() {
+        // First parent is discarded => look for more parents
+        if(p2OrParents instanceof QueryIdentityHashSet) {
+
+            QueryIdentityHashSet set = (QueryIdentityHashSet)p2OrParents;
+            set.removeDiscardedReally();
+            if(set.isEmpty()) p2OrParents = null;
+
+        } else if(p2OrParents instanceof CacheEntry) {
+
+            CacheEntry entry = (CacheEntry)p2OrParents;
+            if(entry.isDiscarded()) {
+                // Second entry is also discarded => all empty
+                p2OrParents = null;
+            }
+
+        } else {
+
+            // Nothing left
+
+        }
+    }
+
     @Override
     final public void removeParent(CacheEntry entry) {
        
@@ -318,9 +364,9 @@ abstract public class CacheEntryBase extends CacheEntry {
     }
     
     @Override
-       final public Iterable<CacheEntry> getParents(QueryProcessor processor) {
+       final public Collection<CacheEntry<?>> getParents(QueryProcessor processor) {
 
-               ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();
+               ArrayList<CacheEntry<?>> result = new ArrayList<CacheEntry<?>>();
                if(p1 != null) result.add(p1);
                if(p2OrParents != null) {
                if(p2OrParents instanceof QueryIdentityHashSet) {
@@ -360,8 +406,7 @@ abstract public class CacheEntryBase extends CacheEntry {
        
     }
     
-    protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry> result) {
-       
+    protected void fillImpliedParents(QueryProcessor processor, ArrayList<CacheEntry<?>> result) {
     }
     
     protected String internalError() {
@@ -369,7 +414,7 @@ abstract public class CacheEntryBase extends CacheEntry {
     }
 
     
-    protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) {
+    protected boolean handleException(ReadGraphImpl graph, IntProcedure procedure) throws DatabaseException {
        if(isExcepted()) {
                procedure.exception(graph, (Throwable)getResult());
                return true;
@@ -378,7 +423,7 @@ abstract public class CacheEntryBase extends CacheEntry {
        }
     }
     
-    protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) {
+    protected boolean handleException(ReadGraphImpl graph, TripleIntProcedure procedure) throws DatabaseException {
        if(isExcepted()) {
                procedure.exception(graph, (Throwable)getResult());
                return true;
@@ -387,7 +432,7 @@ abstract public class CacheEntryBase extends CacheEntry {
        }
     }
 
-    protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) {
+    protected <T> boolean handleException(ReadGraphImpl graph, InternalProcedure<T> procedure) throws DatabaseException {
        if(isExcepted()) {
                procedure.exception(graph, (Throwable)getResult());
                return true;
@@ -420,14 +465,9 @@ abstract public class CacheEntryBase extends CacheEntry {
     
     @Override
     void prepareRecompute(QuerySupport querySupport) {
-               setPending();
-               clearResult(querySupport);
+        setPending(querySupport);
     }
     
-    /*
-     * 
-     * 
-     */
     @Override
     int getGCStatus() {
        return GCStatus;
@@ -453,5 +493,47 @@ abstract public class CacheEntryBase extends CacheEntry {
        // This is the original request for all built-in queries
        return getQuery();
     }
+
+    public CacheEntryBase() {
+    }
     
+    public String classId() {
+        return getClass().getName();
+    }
+
+    public void serializeKey(QuerySerializer serializer) {
+        throw new IllegalStateException("Cannot serialize query key for " + this);
+    }
+    
+    public void serializeValue(QuerySerializer serializer) {
+        throw new IllegalStateException("Cannot serialize query value for " + this);
+    }
+    
+    public void serializeParents(QuerySerializer serializer) {
+        Collection<CacheEntry<?>> ps = getParents(serializer.getQueryProcessor());
+        int sizePos = serializer.writeUnknownSize();
+        int actual = 0;
+        for(CacheEntry<?> entry : ps) {
+            CacheEntryBase b = (CacheEntryBase)entry;
+            String cid = b.classId();
+            if(cid == null) 
+                continue;
+            serializer.serializeId(b.classId());
+            b.serializeKey(serializer);
+            actual++;
+        }
+        serializer.setUnknownSize(sizePos, actual);
+    }
+
+    public long cluster(QueryProcessor processor) {
+        throw new IllegalStateException("Cannot compute query cluster for " + this);
+    }
+
+    public void serialize(QuerySerializer serializer) {
+        serializer.serializeId(classId());
+        serializeKey(serializer);
+        serializeValue(serializer);
+        serializeParents(serializer);
+    }
+
 }