--- /dev/null
+package org.simantics.acorn.lru;
+
+import java.nio.file.Path;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.simantics.acorn.ClusterManager;
+import org.simantics.acorn.cluster.ClusterImpl;
+import org.simantics.acorn.internal.BijectionMap;
+import org.simantics.db.common.utils.Logger;
+import org.simantics.db.exception.ClusterDoesNotExistException;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.impl.ClusterBase;
+import org.simantics.db.impl.ClusterI;
+import org.simantics.db.service.ClusterUID;
+
+import gnu.trove.TIntIntHashMap;
+
+public class ClusterLRU extends LRU<ClusterUID, ClusterInfo> {
+
+ final private BijectionMap<ClusterUID, Integer> clusterMapping = new BijectionMap<ClusterUID, Integer>();
+ final private ClusterManager manager;
+
+ public ClusterLRU(ClusterManager manager, String identifier, Path writeDir) {
+
+ super(identifier, writeDir);
+ this.manager = manager;
+
+ clusterMapping.map(ClusterUID.make(0,2), clusterMapping.size() + 1);
+
+ }
+
+ public ClusterInfo getOrCreate(ClusterUID uid, boolean makeIfNull) {
+
+ try {
+
+ acquireMutex();
+
+ ClusterInfo info = get(uid);
+
+ if (info == null) {
+
+ if(!makeIfNull) throw new IllegalStateException("Asked for an existing cluster " + uid + " that was not found.");
+
+ Integer clusterKey = clusterMapping.getRight(uid);
+ if (clusterKey == null) {
+ clusterKey = clusterMapping.size() + 1;
+ clusterMapping.map(uid, clusterKey);
+ }
+
+ info = new ClusterInfo(manager, this, ClusterImpl.make(manager.support,
+ uid, clusterKey, manager.support));
+
+ }
+
+ return info;
+
+ } catch (Throwable t) {
+ throw new IllegalStateException(t);
+ } finally {
+
+ releaseMutex();
+
+ }
+
+ }
+
+ /*
+ * This method waits - we have no locks here
+ */
+ public void ensureUpdates(ClusterUID uid) throws DatabaseException {
+
+ ClusterInfo info = getWithoutMutex(uid);
+ if(info == null)
+ throw new ClusterDoesNotExistException("Asked a cluster which does not exist: " + uid);
+ info.waitForUpdates();
+
+ }
+
+ public ClusterInfo get(ClusterUID uid, boolean makeIfNull, boolean ensureUpdates) throws DatabaseException {
+
+ if (ensureUpdates) {
+ try {
+ ensureUpdates(uid);
+ } catch (ClusterDoesNotExistException e) {
+ if (makeIfNull) {
+ Logger.defaultLogError("For debug purposes, creating cluster which does not exist", e);
+ } else {
+ throw e;
+ }
+ }
+ }
+ return getOrCreate(uid, makeIfNull);
+ }
+
+ public ClusterInfo get(ClusterUID uid, boolean makeIfNull) throws DatabaseException {
+ return get(uid, makeIfNull, true);
+
+ }
+
+ public int getResourceKey(ClusterUID uid, int index) {
+
+ if(VERIFY) verifyAccess();
+
+ Integer i = clusterMapping.getRight(uid);
+ if (i == null) {
+ i = clusterMapping.size() + 1;
+ clusterMapping.map(uid, i);
+ }
+ return (i << 12) + index;
+
+ }
+
+ public int getResourceKeyWithoutMutex(ClusterUID uid, int index) {
+
+ acquireMutex();
+ try {
+ return getResourceKey(uid, index);
+ } catch (Throwable t) {
+ throw new IllegalStateException(t);
+ } finally {
+ releaseMutex();
+ }
+
+ }
+
+ public int createClusterKeyByClusterUID(ClusterUID uid) {
+
+ if(VERIFY) verifyAccess();
+
+ Integer i = clusterMapping.getRight(uid);
+ if (i == null) {
+ i = clusterMapping.size() + 1;
+ clusterMapping.map(uid, i);
+ }
+ return i;
+
+ }
+
+ public ClusterBase getClusterByClusterUIDOrMake(ClusterUID uid) throws DatabaseException {
+
+ if(VERIFY) verifyAccess();
+
+ int key = createClusterKeyByClusterUID(uid);
+ return getClusterByClusterKey(key);
+
+ }
+
+ public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) {
+
+ if(VERIFY) verifyAccess();
+
+ return createClusterKeyByClusterUID(clusterUID);
+
+ }
+
+ public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) {
+ acquireMutex();
+ try {
+ return getClusterKeyByClusterUIDOrMake(clusterUID);
+ } catch (Throwable t) {
+ throw new IllegalStateException(t);
+ } finally {
+ releaseMutex();
+ }
+ }
+
+ public ClusterBase getClusterByClusterKey(int clusterKey) throws DatabaseException {
+
+ if(VERIFY) verifyAccess();
+
+ ClusterUID uid = clusterMapping.getLeft(clusterKey);
+ ClusterInfo info = get(uid, true);
+ info.acquireMutex();
+ try {
+ return info.getCluster();
+ } catch (Throwable t) {
+ throw new IllegalStateException(t);
+ } finally {
+ info.releaseMutex();
+ }
+
+ }
+
+ public ClusterUID getClusterUIDByResourceKey(int resourceKey)
+ throws DatabaseException {
+
+ if(VERIFY) verifyAccess();
+
+ int clusterKey = resourceKey >> 12;
+ return clusterMapping.getLeft(clusterKey);
+
+ }
+
+ public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws DatabaseException {
+ acquireMutex();
+ try {
+ return getClusterUIDByResourceKey(resourceKey);
+ } catch (Throwable t) {
+ throw new IllegalStateException(t);
+ } finally {
+ releaseMutex();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends ClusterI> T getClusterByClusterUIDOrMakeProxy(ClusterUID uid) throws DatabaseException {
+ return (T) getClusterByClusterUIDOrMake(uid);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends ClusterI> T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException {
+
+ if(VERIFY) verifyAccess();
+
+ return (T) getClusterByClusterKey(resourceKey >> 12);
+
+ }
+
+ public int getClusterKeyByUID(long id1, long id2) throws DatabaseException {
+
+ if(VERIFY) verifyAccess();
+
+ return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2));
+
+ }
+
+ public int getClusterKeyByUIDWithoutMutex(long id1, long id2) throws DatabaseException {
+
+ acquireMutex();
+ try {
+ return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2));
+ } catch (Throwable t) {
+ throw new IllegalStateException(t);
+ } finally {
+ releaseMutex();
+ }
+
+ }
+
+
+ public static void main(String[] args) throws Exception {
+
+ long start = System.nanoTime();
+
+ final TIntIntHashMap map = new TIntIntHashMap(0, 0.9f);
+
+ AtomicInteger counter = new AtomicInteger(0);
+ AtomicBoolean written = new AtomicBoolean(false);
+
+ //final Semaphore ws = new Semaphore(1);
+
+ Thread write = new Thread() {
+
+ @Override
+ public void run() {
+ try {
+ for(int i=0;i<100000000;i++) {
+ synchronized(map) {
+// ws.acquire();
+ map.put(i, i);
+// ws.release();
+ }
+ //if((i & 0xfffff) == 0) System.err.println("Write " + i);
+ counter.incrementAndGet();
+ }
+ written.set(true);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ };
+ write.start();
+
+ Thread read = new Thread() {
+
+ @Override
+ public void run() {
+ try {
+ while(!written.get()) {
+ double r = Math.random();
+ double max = counter.get();
+ int key = (int)(max*r);
+ int value = map.get(key);
+ if(key != value) {
+ //System.err.println("Read failed " + key + " vs. " + value);
+ //ws.acquire();
+ synchronized(map) {
+ value = map.get(key);
+ if(key != value) {
+ System.err.println("Read failed for real " + key + " vs. " + value);
+ }
+ //ws.release();
+ }
+ }
+ //if((key & 0xfffff) == 0) System.err.println("Read " + key);
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ };
+ read.start();
+
+ write.join();
+ read.join();
+
+ long duration = System.nanoTime() - start;
+ System.err.println("took " + 1e-9*duration + "s.");
+
+ }
+
+}