]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryCollectorImpl2.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / QueryCollectorImpl2.java
1 package org.simantics.db.impl.query;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.IdentityHashMap;
6 import java.util.List;
7 import java.util.Map;
8
9 import org.simantics.databoard.Bindings;
10 import org.simantics.databoard.util.IdentityHashSet;
11 import org.simantics.db.DevelopmentKeys;
12 import org.simantics.db.common.exception.DebugException;
13 import org.simantics.db.impl.query.QueryProcessor.QueryCollectorSupport;
14 import org.simantics.utils.Development;
15
16 class QueryCollectorImpl2 implements QueryProcessor.QueryCollector {
17
18         private final QueryProcessor queryProcessor;
19
20         final private QueryCollectorSupport support;
21
22         private int lastKnownFixedSize = 0;
23
24         QueryCollectorImpl2(QueryProcessor queryProcessor, QueryCollectorSupport support) {
25                 this.queryProcessor = queryProcessor;
26                 this.support = support;
27         }
28
29         private boolean findCollectables(CacheEntry<?> entry, Map<CacheEntry, Boolean> collectables, ArrayList<CacheEntry> result) {
30
31                 if (entry.isDiscarded()) {
32                         if (Development.DEVELOPMENT) {
33                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
34                                         System.err.println("GC: discarded entry " + entry);
35                                 }
36                         }
37                         return true;
38                 }
39
40                 if (entry.isPending()) {
41                         if (Development.DEVELOPMENT) {
42                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
43                                         System.err.println("GC: pending entry " + entry + " was not collected.");
44                                 }
45                         }
46                         collectables.remove(entry);
47                         return false;
48                 }
49
50                 if (this.queryProcessor.listening.hasListenerAfterDisposing(entry)) {
51                         if (Development.DEVELOPMENT) {
52                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
53                                         System.err.println("GC: listened entry " + entry + " was not collected. Entry=" + entry);
54                                 }
55                         }
56                         collectables.remove(entry);
57                         return false;
58                 }
59
60                 for (CacheEntry parent : entry.getParents(queryProcessor)) {
61
62                         boolean parentIsCollectable = false;
63
64                         if (!collectables.containsKey(parent)) {
65                                 collectables.put(parent, true);
66                                 parentIsCollectable = findCollectables(parent, collectables, result);
67                         } else {
68                                 parentIsCollectable = collectables.get(parent);
69                         }
70
71                         if(!parentIsCollectable) {
72                                 if (Development.DEVELOPMENT) {
73                                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
74                                                 System.err.println("GC: due to bound parent " + parent + " the entry + " + entry + " was not collected.");
75                                         }
76                                 }
77                                 collectables.remove(entry);
78                                 return false;
79                         }
80
81                 }
82
83                 if(entry.shouldBeCollected()) {
84                         // This can be collected
85                         result.add(entry);
86                         return true;
87                 } else {
88                         return false;
89                 }
90
91         }
92
93         private List<CacheEntry> findCollectables() {
94
95                 ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();
96
97                 IdentityHashMap<CacheEntry, Boolean> collectables = new IdentityHashMap<CacheEntry, Boolean>();
98                 Collection<CacheEntry> rootList = support.getRootList();
99                 for (CacheEntry entry : rootList) {
100                         if(!collectables.containsKey(entry)) {
101                                 collectables.put(entry, true);
102                                 findCollectables(entry, collectables, result);
103                         }
104                 }
105
106                 if(Development.DEVELOPMENT) {
107                         IdentityHashSet<CacheEntry> set = new IdentityHashSet<CacheEntry>();
108                         for(CacheEntry entry : result) {
109                                 if(!set.add(entry)) throw new IllegalStateException();
110                         }
111                 }
112
113                 return result;
114
115         }
116
117         private void doCollect(int currentSize, int maxNumberOfCollectableQueries) {
118
119                 List<CacheEntry> collectables = findCollectables();
120
121                 // Compute amount of free queries
122                 int freeCount = collectables.size();
123
124                 if (Development.DEVELOPMENT) {
125                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
126                                 System.err.println("collector found " + freeCount + " free queries.");
127                         }
128                 }
129
130                 lastKnownFixedSize = currentSize - freeCount;
131
132                 // No need to collect
133                 if(freeCount < maxNumberOfCollectableQueries) return;
134
135                 int target = freeCount - maxNumberOfCollectableQueries/2;
136
137                 
138                 if (Development.DEVELOPMENT) {
139                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
140                                 System.err.println("collector found " + freeCount + " free queries.");
141                                 System.err.println("collector removes " + target + " free queries.");
142                         }
143                 }
144
145                 for(CacheEntry entry : collectables) {
146                         if(queryProcessor.removeQuery(entry))
147                                 if(--target < 0) break;
148                 }
149
150                 // Prune discarded parents
151                 ArrayList<CacheEntry> removals = new ArrayList<CacheEntry>();
152                 for (CacheEntry<?> entry : support.allCaches().toCollection()) {
153                         for(CacheEntry p : entry.getParents(queryProcessor)) {
154                                 if(p.isDiscarded()) removals.add(p);
155                         }
156                         for(CacheEntry r : removals) {
157                                 entry.removeParent(r);
158                         }
159                         removals.clear();
160                 }
161
162                 
163                 if (Development.DEVELOPMENT) {
164                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
165                                 System.err.println("collect found " + freeCount + " collectable entries.");
166                         }
167                 }
168
169                 return;
170
171         }
172
173         @Override
174         public void collect(int youngTarget, int maxAllowedTimeInMs) {
175
176                 try {
177
178
179                         int current = support.calculateCurrentSize();
180
181                         if (Development.DEVELOPMENT) {
182                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
183                                         new DebugException("checking the need for collecting queries (current=" + current + " , lastKnownFixedSize=" + lastKnownFixedSize + " max free=" + 0 + ")").printStackTrace();
184                                 }
185                         }
186                         
187                         queryProcessor.cache.collecting = true;
188
189                         long start = System.nanoTime();
190
191                         doCollect(current, 0);
192
193                         if (Development.DEVELOPMENT) {
194                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
195                                         System.err.println("collect finished with " + support.calculateCurrentSize() + " entries (lastKnownFixedSize=" + lastKnownFixedSize + ").");
196                                 }
197                         }
198
199                         long duration = System.nanoTime() - start;
200
201                         if (Development.DEVELOPMENT) {
202                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYCOLLECTOR, Bindings.BOOLEAN)) {
203                                         System.err.println("Collect took " + 1e-9*duration + "s.");
204                                 }
205                         }
206
207                 } catch (Throwable t) {
208                         t.printStackTrace();
209                 }
210
211                 queryProcessor.cache.collecting = false;
212
213         }
214
215
216 }