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.exception.AcornAccessVerificationException; import org.simantics.acorn.exception.IllegalAcornStateException; 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 { final private BijectionMap clusterMapping = new BijectionMap(); public ClusterLRU(ClusterManager manager, String identifier, Path writeDir) { super(manager, identifier, writeDir); clusterMapping.map(ClusterUID.make(0,2), clusterMapping.size() + 1); } public ClusterInfo getOrCreate(ClusterUID uid, boolean makeIfNull) throws IllegalAcornStateException, AcornAccessVerificationException { try { acquireMutex(); ClusterInfo info = get(uid); if (info == null) { if(!makeIfNull) throw new IllegalAcornStateException("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 (IllegalAcornStateException | AcornAccessVerificationException e) { throw e; } catch (Throwable t) { throw new IllegalAcornStateException(t); } finally { releaseMutex(); } } /* * This method waits - we have no locks here */ public void ensureUpdates(ClusterUID uid) throws ClusterDoesNotExistException, AcornAccessVerificationException, IllegalAcornStateException { 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 AcornAccessVerificationException, IllegalAcornStateException { if (ensureUpdates) { try { ensureUpdates(uid); } catch (ClusterDoesNotExistException e) { if (makeIfNull) { Logger.defaultLogError("For debug purposes, creating cluster which does not exist", e); } else { throw new IllegalAcornStateException(e); } } } return getOrCreate(uid, makeIfNull); } public ClusterInfo get(ClusterUID uid, boolean makeIfNull) throws AcornAccessVerificationException, IllegalAcornStateException { return get(uid, makeIfNull, true); } public int getResourceKey(ClusterUID uid, int index) throws AcornAccessVerificationException { 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) throws IllegalAcornStateException { acquireMutex(); try { return getResourceKey(uid, index); } catch (Throwable t) { throw new IllegalAcornStateException(t); } finally { releaseMutex(); } } public int createClusterKeyByClusterUID(ClusterUID uid) throws AcornAccessVerificationException { 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 AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); int key = createClusterKeyByClusterUID(uid); return getClusterByClusterKey(key); } public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); return createClusterKeyByClusterUID(clusterUID); } public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) throws IllegalAcornStateException, AcornAccessVerificationException { acquireMutex(); try { return getClusterKeyByClusterUIDOrMake(clusterUID); } catch (AcornAccessVerificationException e) { throw e; } catch (Throwable t) { throw new IllegalAcornStateException(t); } finally { releaseMutex(); } } public ClusterBase getClusterByClusterKey(int clusterKey) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); ClusterUID uid = clusterMapping.getLeft(clusterKey); ClusterInfo info = get(uid, true); info.acquireMutex(); try { return info.getCluster(); } catch (IllegalAcornStateException | AcornAccessVerificationException e) { throw e; } catch (Throwable t) { throw new IllegalAcornStateException(t); } finally { info.releaseMutex(); } } public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); int clusterKey = resourceKey >> 12; return clusterMapping.getLeft(clusterKey); } public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws IllegalAcornStateException, AcornAccessVerificationException { acquireMutex(); try { return getClusterUIDByResourceKey(resourceKey); } finally { releaseMutex(); } } @SuppressWarnings("unchecked") public T getClusterByClusterUIDOrMakeProxy(ClusterUID uid) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { return (T) getClusterByClusterUIDOrMake(uid); } @SuppressWarnings("unchecked") public T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); return (T) getClusterByClusterKey(resourceKey >> 12); } public int getClusterKeyByUID(long id1, long id2) throws DatabaseException, AcornAccessVerificationException { if(VERIFY) verifyAccess(); return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2)); } public int getClusterKeyByUIDWithoutMutex(long id1, long id2) throws DatabaseException, IllegalAcornStateException { acquireMutex(); try { return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2)); } catch (Throwable t) { throw new IllegalAcornStateException(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."); } }