Fixed multiple issues causing dangling references to discarded queries 19/4419/4 master
authorJussi Koskela <jussi.koskela@semantum.fi>
Wed, 2 Sep 2020 09:17:31 +0000 (12:17 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Thu, 8 Oct 2020 07:56:28 +0000 (07:56 +0000)
gitlab #594

Change-Id: Iaa6b12f60d7dfa2bbcbc9614ef837973885586cc

bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntry.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/CacheEntryBase.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/ExternalReadEntry.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCollectorImpl.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHash.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryIdentityHashSet.java
bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryListening.java
bundles/org.simantics.document.server/src/org/simantics/document/server/request/DocumentRequest.java
bundles/org.simantics.document.server/src/org/simantics/document/server/request/NodesRequest.java

index edb0870254f327ebba56d297c2a166a40003efd2..a650f32ce8dc6ee1f70166cbb3e21c31b6604f17 100644 (file)
@@ -42,6 +42,7 @@ public abstract class CacheEntry<Procedure> {
     abstract Query getQuery();
 
     abstract CacheEntry pruneFirstParents();
+    abstract void pruneParentSet();
     abstract void removeParent(CacheEntry entry);
     abstract void addParent(CacheEntry entry);
     abstract boolean hasParents();
index 56da11ace36b718d75ce3fa48ac9012c6f3eedf0..98e7f0d00554f1c3f351339d8aa4b891028966ae 100644 (file)
@@ -278,7 +278,31 @@ public abstract class CacheEntryBase<Procedure> extends CacheEntry<Procedure> {
        }
         
     }
-    
+
+    @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) {
        
index 6253744bb05e26d012ff3b86906e465fc9911e95..2980b9b15f72e9a54b00a96fe06a397ef60b59b6 100644 (file)
@@ -28,7 +28,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
     final LinkedList<T> items = new LinkedList<T>();
 
     protected ExternalRead<T> id;
-    protected ReadGraphImpl graph;
+    protected QueryProcessor processor;
     protected boolean registered = false;
 
     @Override
@@ -49,7 +49,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
     public void discard() {
         id.unregistered();
         id = null;
-        graph = null;
+        processor = null;
         super.discard();
     }
 
@@ -65,7 +65,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
     public ExternalReadEntry(ExternalRead<T> request, ReadGraphImpl graph) {
         assert request != null;
         this.id = request;
-        this.graph = graph;
+        this.processor = graph.processor;
     }
     
     @Override
@@ -213,7 +213,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
 
                synchronized(items) {
                        items.addLast(result);
-                               graph.processor.updatePrimitive(id);
+                               processor.updatePrimitive(id);
                        // TODO: implement flags/logic in ExternalRead to state that all but the latest request result can be evaporated
                        // In some cases where data is produced really fast this might be necessary but currently this queueing will do.
                }
@@ -229,7 +229,7 @@ final public class ExternalReadEntry<T> extends CacheEntryBase<AsyncProcedure<T>
 
        @Override
        public boolean isDisposed() {
-               return registered && (isDiscarded() || !graph.processor.isBound(this));
+               return registered && (isDiscarded() || !processor.isBound(this));
        }
 
     @Override
index 0b6f6ef834411316255ac12e80f56faa3d3bdd93..e6dc252c41f210d00586895aeabe6d7c619001f3 100644 (file)
@@ -148,6 +148,7 @@ class QueryCollectorImpl implements QueryProcessor.QueryCollector {
                                                        
                                                } else {
                                                        
+                                                       entry.pruneParentSet();
                                                        support.setLevel(entry, parent.getLevel() + 1);
                                                        
                                                }
index ffbf4b2a9a34f2f20814b2e17df54a6ee66f349b..2db67c67970b165f836317cbbaaabea40fa6994d 100644 (file)
@@ -229,7 +229,11 @@ abstract public class QueryIdentityHash extends THash {
                        // TODO Auto-generated method stub
                        return null;
                }
-        
+
+        @Override
+            void pruneParentSet() {
+        }
+
     };
 
     /**
index 3a7eb61820203ffc5e079d0a57b12f7fe8c86842..ac2602fcb647b4ad54e8de9edf1a8c4412313d95 100644 (file)
@@ -147,7 +147,25 @@ final public class QueryIdentityHashSet extends QueryIdentityHash implements Ite
         }
         
     }
-    
+
+    final public void removeDiscardedReally() {
+
+        tempDisableAutoCompaction();
+        try {
+
+            for(int i=0;i<_set.length;i++) {
+                CacheEntry entry = _set[i];
+                if(entry != null && REMOVED != entry) {
+                    if(entry.isDiscarded()) removeAt(i);
+                }
+            }
+
+        } finally {
+            reenableAutoCompaction(false);
+        }
+
+    }
+
     /**
      * Creates an iterator over the values of the set.  The iterator
      * supports element deletion.
index 91200f1fc3ad037393bc5e3794d6acc4c29a4e7b..e0b39d6516055656995689f90c1506c19bb26265 100644 (file)
@@ -415,7 +415,7 @@ public class QueryListening {
 
         @Override
         public void run() {
-            ListenerEntry entry = addedEntries.get(base);
+            ListenerEntry entry = addedEntries.remove(base);
             if(entry != null) entry.setLastKnown(result);
         }
 
index 8acc2f0d192899316c870cfba8ce27366b93d699..5b76cd344043f10dd05428642bee688c418ed12f 100644 (file)
@@ -1,6 +1,7 @@
 package org.simantics.document.server.request;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
@@ -10,7 +11,7 @@ import java.util.Set;
 import org.simantics.db.AsyncReadGraph;
 import org.simantics.db.ReadGraph;
 import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener;
-import org.simantics.db.common.request.AsyncReadRequest;
+import org.simantics.db.common.request.UnaryAsyncRead;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.request.VariableRead;
 import org.simantics.db.layer0.variable.Variable;
@@ -28,49 +29,55 @@ public class DocumentRequest extends VariableRead<List<JSONObject>> {
         super(var);
        }
 
-       @Override
-       public List<JSONObject> perform(ReadGraph graph) throws DatabaseException {
-               
-               long s = System.nanoTime();
-               
-        Set<Variable> nodes = graph.syncRequest(new NodesRequest(variable), TransientCacheAsyncListener.<Set<Variable>>instance());
-        HashSet<JSONObject> rs = new HashSet<JSONObject>(); // result
-        if(nodes.isEmpty()) {
-            return Collections.emptyList();
-        }
+    static class CollectNodesRequest extends UnaryAsyncRead<Collection<Variable>, Collection<JSONObject>> {
 
-        if(PROFILE) {
-            long dura = System.nanoTime()-s;
-            System.err.println("DocumentRequest1 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph));
+        public CollectNodesRequest(Collection<Variable> nodes) {
+            super(nodes);
         }
 
-        graph.syncRequest(new AsyncReadRequest() {
+        @Override
+        public void perform(AsyncReadGraph graph, AsyncProcedure<Collection<JSONObject>> procedure) {
+            HashSet<JSONObject> rs = new HashSet<JSONObject>(); // result
 
-            @Override
-            public void run(AsyncReadGraph graph) throws DatabaseException {
+            for(Variable node : parameter) {
+                graph.asyncRequest(new NodeRequest(node), new AsyncProcedure<JSONObject> () {
 
-                for(Variable node : nodes) {
-                    graph.asyncRequest(new NodeRequest(node), new AsyncProcedure<JSONObject> () {
-
-                        @Override
-                        public void execute(AsyncReadGraph graph, JSONObject result) {
-                            synchronized (rs) {
-                                rs.add(result);
-                            }
+                    @Override
+                    public void execute(AsyncReadGraph graph, JSONObject result) {
+                        synchronized(rs) {
+                            rs.add(result);
                         }
+                    }
 
-                        @Override
-                        public void exception(AsyncReadGraph graph, Throwable throwable) {
-                        }
+                    @Override
+                    public void exception(AsyncReadGraph graph, Throwable throwable) {
+                    }
+
+                });
 
-                    });
-                    
-                }
-                
             }
-            
-        });
+            procedure.execute(graph, rs);
+
+        }
+
+    }
+
+    @Override
+    public List<JSONObject> perform(ReadGraph graph) throws DatabaseException {
+
+        long s = System.nanoTime();
+
+        Set<Variable> nodes = graph.syncRequest(new NodesRequest(variable), TransientCacheAsyncListener.<Set<Variable>>instance());
+        if(nodes.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        if(PROFILE) {
+            long dura = System.nanoTime()-s;
+            System.err.println("DocumentRequest1 " + System.identityHashCode(this) + " in " + 1e-6*dura + "ms. " + variable.getURI(graph));
+        }
 
+        Collection<JSONObject> rs = graph.syncRequest(new CollectNodesRequest(nodes));
 
         if(PROFILE) {
             long dura = System.nanoTime()-s;
index 99526368e8cc3e21e1b92451f4dd915db45d40ba..133264e29b09321e399831600106ce0227ba0a04 100644 (file)
@@ -2,11 +2,12 @@ package org.simantics.document.server.request;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
 
 import org.simantics.db.AsyncReadGraph;
 import org.simantics.db.ReadGraph;
-import org.simantics.db.common.request.AsyncReadRequest;
+import org.simantics.db.common.request.UnaryAsyncRead;
 import org.simantics.db.exception.DatabaseException;
 import org.simantics.db.layer0.request.VariableChildren;
 import org.simantics.db.layer0.request.VariableRead;
@@ -16,52 +17,57 @@ import org.simantics.structural.stubs.StructuralResource2;
 import org.simantics.utils.threads.logger.ITask;
 import org.simantics.utils.threads.logger.ThreadLogger;
 
-import gnu.trove.set.hash.THashSet;
-
 public class NodesRequest extends VariableRead<Set<Variable>> {
 
     public NodesRequest(Variable var) {
         super(var);
     }
 
-    @Override
-    public Set<Variable> perform(ReadGraph graph) throws DatabaseException {
+    static class CollectNodesRequest2 extends UnaryAsyncRead<Collection<Variable>, Set<Variable>> {
 
-        ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null;
+        public CollectNodesRequest2(Collection<Variable> nodes) {
+            super(nodes);
+        }
 
-        StructuralResource2.getInstance(graph);
-        if(variable == null)
-            return Collections.emptySet();
+        @Override
+        public void perform(AsyncReadGraph graph, AsyncProcedure<Set<Variable>> procedure) {
+            HashSet<Variable> rs = new HashSet<Variable>(); // result
 
-        Set<Variable> nodes = new THashSet<Variable>();
+            for(Variable node : parameter) {
+                graph.asyncRequest(new NodesRequest2(node), new AsyncProcedure<Set<Variable>> () {
 
-        Collection<Variable> children = graph.syncRequest(new VariableChildren(variable));
+                    @Override
+                    public void execute(AsyncReadGraph graph, Set<Variable> result) {
+                        synchronized(rs) {
+                            rs.addAll(result);
+                        }
+                    }
 
-        graph.syncRequest(new AsyncReadRequest() {
+                    @Override
+                    public void exception(AsyncReadGraph graph, Throwable throwable) {
+                    }
 
-            @Override
-            public void run(AsyncReadGraph graph) throws DatabaseException {
+                });
 
-                for(Variable child : children) {
-                    graph.asyncRequest(new NodesRequest2(child), new AsyncProcedure<Set<Variable>>() {
+            }
+            procedure.execute(graph, rs);
 
-                        @Override
-                        public void execute(AsyncReadGraph graph, Set<Variable> result) {
-                            synchronized(nodes) {
-                                nodes.addAll(result);
-                            }
-                        }
+        }
 
-                        @Override
-                        public void exception(AsyncReadGraph graph, Throwable throwable) {
-                        }
-                        
-                    });
-                }
+    }
 
-            }
+    @Override
+    public Set<Variable> perform(ReadGraph graph) throws DatabaseException {
+
+        ITask task = DocumentRequest.PROFILE ? ThreadLogger.task(this) : null;
+
+        StructuralResource2.getInstance(graph);
+        if(variable == null)
+            return Collections.emptySet();
+
+        Collection<Variable> children = graph.syncRequest(new VariableChildren(variable));
 
-        });
+        Set<Variable> nodes = graph.syncRequest(new CollectNodesRequest2(children));
 
         if(DocumentRequest.PROFILE) task.finish();