package org.simantics.db.impl.query; import java.util.ArrayList; import java.util.Collection; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import org.simantics.databoard.Bindings; import org.simantics.databoard.util.IdentityHashSet; import org.simantics.db.DevelopmentKeys; import org.simantics.db.common.exception.DebugException; import org.simantics.db.impl.query.QueryProcessor.QueryCollectorSupport; import org.simantics.utils.Development; class QueryCollectorImpl2 implements QueryProcessor.QueryCollector { private final QueryProcessor queryProcessor; final private QueryCollectorSupport support; private int lastKnownFixedSize = 0; QueryCollectorImpl2(QueryProcessor queryProcessor, QueryCollectorSupport support) { this.queryProcessor = queryProcessor; this.support = support; } private boolean findCollectables(CacheEntry entry, Map collectables, ArrayList result) { if (entry.isDiscarded()) { if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("GC: discarded entry " + entry); } } return true; } if (entry.isPending()) { if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("GC: pending entry " + entry + " was not collected."); } } collectables.remove(entry); return false; } if (this.queryProcessor.listening.hasListenerAfterDisposing(entry)) { if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("GC: listened entry " + entry + " was not collected. Entry=" + entry); } } collectables.remove(entry); return false; } for (CacheEntry parent : entry.getParents(queryProcessor)) { boolean parentIsCollectable = false; if (!collectables.containsKey(parent)) { collectables.put(parent, true); parentIsCollectable = findCollectables(parent, collectables, result); } else { parentIsCollectable = collectables.get(parent); } if(!parentIsCollectable) { if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("GC: due to bound parent " + parent + " the entry + " + entry + " was not collected."); } } collectables.remove(entry); return false; } } if(entry.shouldBeCollected()) { // This can be collected result.add(entry); return true; } else { return false; } } private List findCollectables() { ArrayList result = new ArrayList(); IdentityHashMap collectables = new IdentityHashMap(); Collection rootList = support.getRootList(); for (CacheEntry entry : rootList) { if(!collectables.containsKey(entry)) { collectables.put(entry, true); findCollectables(entry, collectables, result); } } if(Development.DEVELOPMENT) { IdentityHashSet set = new IdentityHashSet(); for(CacheEntry entry : result) { if(!set.add(entry)) throw new IllegalStateException(); } } return result; } private void doCollect(int currentSize, int maxNumberOfCollectableQueries) { List collectables = findCollectables(); // Compute amount of free queries int freeCount = collectables.size(); if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("collector found " + freeCount + " free queries."); } } lastKnownFixedSize = currentSize - freeCount; // No need to collect if(freeCount < maxNumberOfCollectableQueries) return; int target = freeCount - maxNumberOfCollectableQueries/2; if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("collector found " + freeCount + " free queries."); System.err.println("collector removes " + target + " free queries."); } } for(CacheEntry entry : collectables) { if(queryProcessor.removeQuery(entry)) if(--target < 0) break; } // Prune discarded parents ArrayList removals = new ArrayList(); for (CacheEntry entry : support.allCaches().toCollection()) { for(CacheEntry p : entry.getParents(queryProcessor)) { if(p.isDiscarded()) removals.add(p); } for(CacheEntry r : removals) { entry.removeParent(r); } removals.clear(); } if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("collect found " + freeCount + " collectable entries."); } } return; } @Override public void collect(int youngTarget, int maxAllowedTimeInMs) { try { int current = support.calculateCurrentSize(); if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { new DebugException("checking the need for collecting queries (current=" + current + " , lastKnownFixedSize=" + lastKnownFixedSize + " max free=" + 0 + ")").printStackTrace(); } } queryProcessor.cache.collecting = true; long start = System.nanoTime(); doCollect(current, 0); if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("collect finished with " + support.calculateCurrentSize() + " entries (lastKnownFixedSize=" + lastKnownFixedSize + ")."); } } long duration = System.nanoTime() - start; if (Development.DEVELOPMENT) { if(Development.getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) { System.err.println("Collect took " + 1e-9*duration + "s."); } } } catch (Throwable t) { t.printStackTrace(); } queryProcessor.cache.collecting = false; } }