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