]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterLRU.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.acorn / src / org / simantics / acorn / lru / ClusterLRU.java
1 package org.simantics.acorn.lru;
2
3 import java.nio.file.Path;
4 import java.util.concurrent.atomic.AtomicBoolean;
5 import java.util.concurrent.atomic.AtomicInteger;
6
7 import org.simantics.acorn.ClusterManager;
8 import org.simantics.acorn.cluster.ClusterImpl;
9 import org.simantics.acorn.exception.AcornAccessVerificationException;
10 import org.simantics.acorn.exception.IllegalAcornStateException;
11 import org.simantics.acorn.internal.BijectionMap;
12 import org.simantics.db.common.utils.Logger;
13 import org.simantics.db.exception.ClusterDoesNotExistException;
14 import org.simantics.db.exception.DatabaseException;
15 import org.simantics.db.impl.ClusterBase;
16 import org.simantics.db.impl.ClusterI;
17 import org.simantics.db.service.ClusterUID;
18 import org.slf4j.LoggerFactory;
19
20 import gnu.trove.map.hash.TIntIntHashMap;
21
22
23 public class ClusterLRU extends LRU<ClusterUID, ClusterInfo> {
24
25     private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ClusterLRU.class);
26     
27         final private BijectionMap<ClusterUID, Integer> clusterMapping = new BijectionMap<ClusterUID, Integer>();
28         
29         public ClusterLRU(ClusterManager manager, String identifier, Path writeDir) {
30                 super(manager, identifier, writeDir);
31                 
32                 clusterMapping.map(ClusterUID.make(0,2), clusterMapping.size() + 1);
33         }
34
35         public ClusterInfo getOrCreate(ClusterUID uid, boolean makeIfNull) throws IllegalAcornStateException, AcornAccessVerificationException {
36                 
37                 try {
38                         
39                         acquireMutex();
40                         
41                         ClusterInfo info = get(uid);
42
43                         if (info == null) {
44                                 
45                                 if(!makeIfNull) throw new IllegalAcornStateException("Asked for an existing cluster " + uid + " that was not found.");
46
47                                 Integer clusterKey = clusterMapping.getRight(uid);
48                                 if (clusterKey == null) {
49                                         clusterKey = clusterMapping.size() + 1;
50                                         clusterMapping.map(uid, clusterKey);
51                                 }
52
53                                 info = new ClusterInfo(manager, this, ClusterImpl.make(manager.support,
54                                                 uid, clusterKey, manager.support));
55
56                         }
57
58                         return info;
59                 } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
60                     throw e;
61                 } catch (Throwable t) {
62                         throw new IllegalAcornStateException(t);
63                 } finally {
64                         
65                         releaseMutex();
66                         
67                 }
68                 
69         }
70
71         /*
72          * This method waits - we have no locks here
73          */
74         public void ensureUpdates(ClusterUID uid) throws ClusterDoesNotExistException, AcornAccessVerificationException, IllegalAcornStateException {
75
76                 ClusterInfo info = getWithoutMutex(uid);
77                 if(info == null)
78                     throw new ClusterDoesNotExistException("Asked a cluster which does not exist: " + uid);
79                 info.waitForUpdates();
80         }
81
82         public ClusterInfo get(ClusterUID uid, boolean makeIfNull, boolean ensureUpdates) throws AcornAccessVerificationException, IllegalAcornStateException {
83
84                 if (ensureUpdates) {
85                     try {
86                         ensureUpdates(uid);
87                     } catch (ClusterDoesNotExistException e) {
88                         if (makeIfNull) {
89                             Logger.defaultLogError("For debug purposes, creating cluster which does not exist", e);
90                         } else {
91                             throw new IllegalAcornStateException(e);
92                         }
93                     }
94                 }
95                 return getOrCreate(uid, makeIfNull);
96         }
97         
98         public ClusterInfo get(ClusterUID uid, boolean makeIfNull) throws AcornAccessVerificationException, IllegalAcornStateException {
99                 return get(uid, makeIfNull, true);
100         }
101
102         public int getResourceKey(ClusterUID uid, int index) throws AcornAccessVerificationException {
103
104                 if(VERIFY) verifyAccess();
105
106                 Integer i = clusterMapping.getRight(uid);
107                 if (i == null) {
108                         i = clusterMapping.size() + 1;
109                         clusterMapping.map(uid, i);
110                 }
111                 return (i << 12) + index;
112
113         }
114
115         public int getResourceKeyWithoutMutex(ClusterUID uid, int index) throws IllegalAcornStateException {
116                 
117                 acquireMutex();
118                 try {
119                         return getResourceKey(uid, index);
120                 } catch (Throwable t) {
121                         throw new IllegalAcornStateException(t);
122                 } finally {
123                         releaseMutex();
124                 }
125         }
126
127         public int createClusterKeyByClusterUID(ClusterUID uid) throws AcornAccessVerificationException {
128                 
129                 if(VERIFY) verifyAccess();
130                 
131                 Integer i = clusterMapping.getRight(uid);
132                 if (i == null) {
133                         i = clusterMapping.size() + 1;
134                         clusterMapping.map(uid, i);
135                 }
136                 return i;
137                 
138         }
139
140         public ClusterBase getClusterByClusterUIDOrMake(ClusterUID uid) throws AcornAccessVerificationException, IllegalAcornStateException {
141                 
142                 if(VERIFY) verifyAccess();
143                 
144                 int key = createClusterKeyByClusterUID(uid);
145                 return getClusterByClusterKey(key);
146                 
147         }
148
149         public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) throws AcornAccessVerificationException {
150
151                 if(VERIFY) verifyAccess();
152
153                 return createClusterKeyByClusterUID(clusterUID);
154                         
155         }
156         
157         public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) throws IllegalAcornStateException, AcornAccessVerificationException {
158                 acquireMutex();
159                 try {
160                         return getClusterKeyByClusterUIDOrMake(clusterUID);
161                 } catch (AcornAccessVerificationException e) {
162             throw e;
163                 } catch (Throwable t) {
164                         throw new IllegalAcornStateException(t);
165                 } finally {
166                         releaseMutex();
167                 }
168         }
169
170         public ClusterBase getClusterByClusterKey(int clusterKey) throws AcornAccessVerificationException, IllegalAcornStateException {
171                 
172                 if(VERIFY) verifyAccess();
173                 
174                 ClusterUID uid = clusterMapping.getLeft(clusterKey);
175                 ClusterInfo info = get(uid, true);
176                 info.acquireMutex();
177                 try {
178                         return info.getCluster();
179                 } catch (IllegalAcornStateException | AcornAccessVerificationException e) {
180                     throw e;
181                 } catch (Throwable t) {
182                         throw new IllegalAcornStateException(t);
183                 } finally {
184                         info.releaseMutex();
185                 }
186         }
187
188         public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws AcornAccessVerificationException {
189                 
190                 if(VERIFY) verifyAccess();
191                 
192                 int clusterKey = resourceKey >> 12;
193                 return clusterMapping.getLeft(clusterKey);
194                 
195         }
196         
197         public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws IllegalAcornStateException, AcornAccessVerificationException {
198                 acquireMutex();
199                 try {
200                         return getClusterUIDByResourceKey(resourceKey);
201                 } finally {
202                         releaseMutex();
203                 }
204         }
205
206         @SuppressWarnings("unchecked")
207         public <T extends ClusterI> T getClusterByClusterUIDOrMakeProxy(ClusterUID uid) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
208                 return (T) getClusterByClusterUIDOrMake(uid);
209         }
210
211         @SuppressWarnings("unchecked")
212         public <T extends ClusterI> T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException {
213                 
214                 if(VERIFY) verifyAccess();
215
216                 return (T) getClusterByClusterKey(resourceKey >> 12);
217                 
218         }
219
220         public int getClusterKeyByUID(long id1, long id2) throws DatabaseException, AcornAccessVerificationException {
221                 
222                 if(VERIFY) verifyAccess();
223
224                 return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2));
225                 
226         }
227         
228         public int getClusterKeyByUIDWithoutMutex(long id1, long id2) throws DatabaseException, IllegalAcornStateException {
229                 acquireMutex();
230                 try {
231                         return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2));
232                 } catch (Throwable t) {
233                         throw new IllegalAcornStateException(t);
234                 } finally {
235                         releaseMutex();
236                 }
237         }
238
239         
240         public static void main(String[] args) throws Exception {
241                 
242                 long start = System.nanoTime();
243                 
244                 final TIntIntHashMap map = new TIntIntHashMap(0, 0.9f);
245
246                 AtomicInteger counter = new AtomicInteger(0);
247                 AtomicBoolean written = new AtomicBoolean(false);
248                 
249                 //final Semaphore ws = new Semaphore(1);
250                 
251                 Thread write = new Thread() {
252                         
253                         @Override
254                         public void run() {
255                                 try {
256                                         for(int i=0;i<100000000;i++) {
257                                                 synchronized(map) {
258 //                                              ws.acquire();
259                                                 map.put(i, i);
260 //                                              ws.release();
261                                                 }
262                                                 //if((i & 0xfffff) == 0) System.err.println("Write " + i);
263                                                 counter.incrementAndGet();
264                                         }
265                                         written.set(true);
266                                 } catch (Throwable e) {
267                                         e.printStackTrace();
268                                 }
269                         }
270                         
271                 };
272                 write.start();
273                 
274                 Thread read = new Thread() {
275                         
276                         @Override
277                         public void run() {
278                                 try {
279                                         while(!written.get()) {
280                                                 double r = Math.random();
281                                                 double max = counter.get();
282                                                 int key = (int)(max*r);
283                                                 int value = map.get(key);
284                                                 if(key != value) {
285                                                         //System.err.println("Read failed " + key + " vs. " + value);
286                                                         //ws.acquire();
287                                                         synchronized(map) {
288                                                                 value = map.get(key);
289                                                                 if(key != value) {
290                                                                         LOGGER.warn("Read failed for real " + key + " vs. " + value);
291                                                                 }
292                                                                 //ws.release();
293                                                         }
294                                                 }
295                                                 //if((key & 0xfffff) == 0) System.err.println("Read " + key);
296                                         }
297                                 } catch (Throwable e) {
298                                         e.printStackTrace();
299                                 }
300                         }
301                         
302                 };
303                 read.start();
304                 
305                 write.join();
306                 read.join();
307                 
308                 if (LOGGER.isDebugEnabled()) {
309                     long duration = System.nanoTime() - start;
310                     LOGGER.debug("took " + 1e-9*duration + "s.");
311                 }
312         }
313         
314 }