From: jsimomaa Date: Tue, 27 Sep 2016 11:59:56 +0000 (+0300) Subject: Acorn: Fix WriteRunnable.runReally() and other fixes X-Git-Tag: v1.25.0~90 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=d9a9d77c7024260e3e3b3a8558ddc84b2a8b99b3;p=simantics%2Fplatform.git Acorn: Fix WriteRunnable.runReally() and other fixes Lets hope this crlf thing is over after this refs #6709 Change-Id: I4e69d8eebf790c2bf4352c1bc8130ba62f126d2f --- diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java index 99ec49062..536c35c74 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornDriver.java @@ -2,8 +2,11 @@ package org.simantics.acorn; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; +import org.simantics.db.Database; import org.simantics.db.DatabaseUserAgent; import org.simantics.db.Driver; import org.simantics.db.ServerI; @@ -16,6 +19,9 @@ public class AcornDriver implements Driver { public static final String AcornDriverName = "acorn"; + private Map servers = new HashMap<>(); + private Map managements = new HashMap<>(); + @Override public String getName() { return AcornDriverName; @@ -23,14 +29,12 @@ public class AcornDriver implements Driver { @Override public DatabaseUserAgent getDatabaseUserAgent(String address) throws DatabaseException { - Path dbFolder = Paths.get(address); - return AcornDatabaseManager.getDatabase(dbFolder).getUserAgent(); + return AcornDatabaseManager.getDatabase(Paths.get(address)).getUserAgent(); } @Override public void setDatabaseUserAgent(String address, DatabaseUserAgent dbUserAgent) throws DatabaseException { - Path dbFolder = Paths.get(address); - AcornDatabaseManager.getDatabase(dbFolder).setUserAgent(dbUserAgent); + AcornDatabaseManager.getDatabase(Paths.get(address)).setUserAgent(dbUserAgent); } @Override @@ -55,7 +59,7 @@ public class AcornDriver implements Driver { } }, null); if (!properties.containsKey("clientId")) - properties.put("clientId", dbFolder.toFile().getAbsolutePath()); + properties.put("clientId", dbFolder.toAbsolutePath().toString()); session.registerService(Properties.class, properties); Session s = session.peekService(Session.class); if (null == s) @@ -65,36 +69,47 @@ public class AcornDriver implements Driver { @Override public ServerI getServer(String address, Properties properties) throws DatabaseException { - return new AcornServerI(address); + ServerI server = servers.get(address); + if (server == null) { + server = new AcornServerI(AcornDatabaseManager.getDatabase(Paths.get(address)), address); + servers.put(address, server); + } + return server; } @Override public Management getManagement(String address, Properties properties) throws DatabaseException { - Path dbFolder = Paths.get(address); - return new AcornManagement(dbFolder, properties); + Management mgmt = managements.get(address); + if (mgmt == null) { + mgmt = new AcornManagement(AcornDatabaseManager.getDatabase(Paths.get(address)), properties); + managements.put(address, mgmt); + } + return mgmt; } private static class AcornServerI implements ServerI { + private Database database; private String address; - public AcornServerI(String address) { + public AcornServerI(Database db, String address) { + this.database = db; this.address = address; } @Override public void stop() throws DatabaseException { - AcornDatabaseManager.getDatabase(Paths.get(address)).tryToStop(); + database.tryToStop(); } @Override public void start() throws DatabaseException { - AcornDatabaseManager.getDatabase(Paths.get(address)).start(); + database.start(); } @Override public boolean isActive() throws DatabaseException { - return AcornDatabaseManager.getDatabase(Paths.get(address)).isRunning(); + return database.isRunning(); } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java index a7bccf087..c21491210 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java @@ -13,8 +13,8 @@ public class AcornManagement implements Management { private final Database db; private final Properties properties; - AcornManagement(Path dbFolder, Properties properties) throws ProCoreException { - db = AcornDatabaseManager.getDatabase(dbFolder); + AcornManagement(Database db, Properties properties) throws ProCoreException { + this.db = db; this.properties = properties; } diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornSessionManagerImpl.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornSessionManagerImpl.java index 1a1e16024..f67a4aa7c 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornSessionManagerImpl.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornSessionManagerImpl.java @@ -13,6 +13,7 @@ import org.simantics.db.common.utils.Logger; import org.simantics.db.event.SessionEvent; import org.simantics.db.event.SessionListener; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.service.LifecycleSupport; import org.simantics.utils.datastructures.ListenerList; @@ -23,8 +24,8 @@ public class AcornSessionManagerImpl implements SessionManager { private static AcornSessionManagerImpl INSTANCE; - private ConcurrentHashMap sessionMap = new ConcurrentHashMap(); - private ListenerList sessionListeners = new ListenerList(SessionListener.class); + private ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); + private ListenerList sessionListeners = new ListenerList<>(SessionListener.class); private SessionErrorHandler errorHandler; private Database database; @@ -51,7 +52,7 @@ public class AcornSessionManagerImpl implements SessionManager { database = AcornDatabaseManager.getDatabase(dbFolder); Database.Session dbSession = database.newSession(sessionImpl); sessionImpl.connect(sessionReference, dbSession); - sessionMap.put(sessionImpl, sessionImpl); + sessionMap.put(sessionImpl, dbSession); fireSessionOpened(sessionImpl); ok = true; } catch (Throwable e) { @@ -91,7 +92,7 @@ public class AcornSessionManagerImpl implements SessionManager { @Override public void shutdown(Session s, Throwable cause) { - SessionImplSocket sis = sessionMap.get(s); + SessionImplSocket sis = (SessionImplSocket) s; if (null == sis) return; try { @@ -122,4 +123,10 @@ public class AcornSessionManagerImpl implements SessionManager { return database; } + public GraphClientImpl2 getClient() { + if (sessionMap.values().size() > 1) + throw new RuntimeDatabaseException("Currently only one GraphClientImpl2 per session is supported!"); + org.simantics.db.Database.Session client = sessionMap.values().iterator().next(); + return (GraphClientImpl2) client; + } } diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java index 5b8e5abb8..51db52efc 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java @@ -9,8 +9,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import org.simantics.acorn.cluster.ClusterImpl; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; +import org.simantics.acorn.exception.InvalidHeadStateException; import org.simantics.acorn.internal.ClusterSupport2; import org.simantics.acorn.lru.ChangeSetInfo; import org.simantics.acorn.lru.ClusterInfo; @@ -19,15 +23,14 @@ import org.simantics.acorn.lru.ClusterStreamChunk; import org.simantics.acorn.lru.FileInfo; import org.simantics.acorn.lru.LRU; import org.simantics.db.ClusterCreator; -import org.simantics.db.ServiceLocator; import org.simantics.db.Database.Session.ClusterIds; import org.simantics.db.Database.Session.ResourceSegment; +import org.simantics.db.ServiceLocator; import org.simantics.db.exception.DatabaseException; import org.simantics.db.impl.ClusterBase; import org.simantics.db.impl.ClusterI; import org.simantics.db.impl.ClusterSupport; import org.simantics.db.procore.cluster.ClusterTraits; -import org.simantics.db.server.ProCoreException; import org.simantics.db.service.ClusterSetsSupport; import org.simantics.db.service.ClusterUID; import org.simantics.utils.threads.logger.ITask; @@ -62,52 +65,50 @@ public class ClusterManager { this.dbFolder = dbFolder; } - public ArrayList getChanges(long changeSetId) { + public ArrayList getChanges(long changeSetId) throws AcornAccessVerificationException, IllegalAcornStateException { ChangeSetInfo info = csLRU.getWithoutMutex(changeSetId); info.acquireMutex(); try { info.makeResident(); return info.getCSSIds(); - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { info.releaseMutex(); } } - public ClusterBase getClusterByClusterKey(int clusterKey) throws DatabaseException { + public ClusterBase getClusterByClusterKey(int clusterKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { return clusterLRU.getClusterByClusterKey(clusterKey); } - public ClusterBase getClusterByClusterUIDOrMake(ClusterUID clusterUID) throws DatabaseException { + public ClusterBase getClusterByClusterUIDOrMake(ClusterUID clusterUID) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { return clusterLRU.getClusterByClusterUIDOrMake(clusterUID); } - public ClusterImpl getClusterByClusterUIDOrMakeProxy(ClusterUID clusterUID) throws DatabaseException { + public ClusterImpl getClusterByClusterUIDOrMakeProxy(ClusterUID clusterUID) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { return clusterLRU.getClusterByClusterUIDOrMakeProxy(clusterUID); } - public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) { + public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) throws AcornAccessVerificationException { return clusterLRU.getClusterKeyByClusterUIDOrMake(clusterUID); } - public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) { + public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) throws IllegalAcornStateException, AcornAccessVerificationException { return clusterLRU.getClusterKeyByClusterUIDOrMakeWithoutMutex(clusterUID); } - public int getClusterKeyByUID(long id1, long id2) throws DatabaseException { + public int getClusterKeyByUID(long id1, long id2) throws DatabaseException, IllegalAcornStateException { return clusterLRU.getClusterKeyByUIDWithoutMutex(id1, id2); } - public T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException { + public T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { return clusterLRU.getClusterProxyByResourceKey(resourceKey); } - public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws DatabaseException { + public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException { return clusterLRU.getClusterUIDByResourceKey(resourceKey); } - public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws DatabaseException { + public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws DatabaseException, IllegalAcornStateException, AcornAccessVerificationException { return clusterLRU.getClusterUIDByResourceKeyWithoutMutex(resourceKey); } @@ -125,69 +126,86 @@ public class ClusterManager { } } - public synchronized boolean makeSnapshot(ServiceLocator locator, boolean force) throws IOException { - - // Maximum autosave frequency is per 60s - if(!force && System.nanoTime() - lastSnapshot < 10*1000000000L) { -// System.err.println("lastSnapshot too early"); - return false; - } - - // Cluster files are always there - // Nothing has been written => no need to do anything - long amountOfFiles = countFiles(workingDirectory); - if(!force && amountOfFiles < 3) { -// System.err.println("amountOfFiles < 3"); - return false; - } - - System.err.println("makeSnapshot"); - - // Schedule writing of all data to disk - refreshHeadState(); - - // Wait for all files to be written - clusterLRU.shutdown(); - fileLRU.shutdown(); - streamLRU.shutdown(); - csLRU.shutdown(); - - persistHeadState(); - - mainState.save(dbFolder); - - ClusterSetsSupport cssi = locator.getService(ClusterSetsSupport.class); - cssi.save(); - - amountOfFiles = countFiles(workingDirectory); - - System.err.println(" -finished: amount of files is " + amountOfFiles); - - workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir)); - if (!Files.exists(workingDirectory)) { - Files.createDirectories(workingDirectory); - } - - cssi.updateReadAndWriteDirectories(lastSessionDirectory, workingDirectory); - - clusterLRU.setWriteDir(workingDirectory); - fileLRU.setWriteDir(workingDirectory); - streamLRU.setWriteDir(workingDirectory); - csLRU.setWriteDir(workingDirectory); - - clusterLRU.resume(); - fileLRU.resume(); - streamLRU.resume(); - csLRU.resume(); - - lastSnapshot = System.nanoTime(); - - return true; - + // Add check to make sure if it safe to make snapshot (used with cancel which is not yet supported and may cause corrupted head.state writing) + private AtomicBoolean safeToMakeSnapshot = new AtomicBoolean(true); + private IllegalAcornStateException cause; + + public synchronized boolean makeSnapshot(ServiceLocator locator, boolean fullSave) throws IllegalAcornStateException { + try { + if (!safeToMakeSnapshot.get()) + throw cause; + // Maximum autosave frequency is per 60s + if(!fullSave && System.nanoTime() - lastSnapshot < 10*1000000000L) { + // System.err.println("lastSnapshot too early"); + return false; + } + + // Cluster files are always there + // Nothing has been written => no need to do anything + long amountOfFiles = countFiles(workingDirectory); + if(!fullSave && amountOfFiles < 3) { + // System.err.println("amountOfFiles < 3"); + return false; + } + + System.err.println("makeSnapshot"); + + // Schedule writing of all data to disk + refreshHeadState(); + + // Wait for all files to be written + clusterLRU.shutdown(); + fileLRU.shutdown(); + streamLRU.shutdown(); + csLRU.shutdown(); + + // Lets check if it is still safe to make a snapshot + if (!safeToMakeSnapshot.get()) + throw cause; + + persistHeadState(); + + if (fullSave) + mainState.save(dbFolder); + + ClusterSetsSupport cssi = locator.getService(ClusterSetsSupport.class); + cssi.save(); + + amountOfFiles = countFiles(workingDirectory); + + System.err.println(" -finished: amount of files is " + amountOfFiles); + + workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir)); + if (!Files.exists(workingDirectory)) { + Files.createDirectories(workingDirectory); + } + + cssi.updateWriteDirectory(workingDirectory); + + clusterLRU.setWriteDir(workingDirectory); + fileLRU.setWriteDir(workingDirectory); + streamLRU.setWriteDir(workingDirectory); + csLRU.setWriteDir(workingDirectory); + + clusterLRU.resume(); + fileLRU.resume(); + streamLRU.resume(); + csLRU.resume(); + + lastSnapshot = System.nanoTime(); + + return true; + } catch (IllegalAcornStateException e) { + notSafeToMakeSnapshot(e); + throw e; + } catch (IOException e) { + IllegalAcornStateException e1 = new IllegalAcornStateException(e); + notSafeToMakeSnapshot(e1); + throw e1; + } } - public void refreshHeadState() throws IOException { - + private void refreshHeadState() throws IOException, IllegalAcornStateException { state.clusters.clear(); state.files.clear(); state.stream.clear(); @@ -197,11 +215,9 @@ public class ClusterManager { fileLRU.persist(state.files); streamLRU.persist(state.stream); csLRU.persist(state.cs); - } - public void persistHeadState() throws IOException { - + private void persistHeadState() throws IOException { // Sync current working directory Files.walk(workingDirectory, 1).filter(Files::isRegularFile).forEach(FileIO::uncheckedSyncPath); state.save(workingDirectory); @@ -241,7 +257,7 @@ public class ClusterManager { // } - private void acquireAll() { + private void acquireAll() throws IllegalAcornStateException { clusterLRU.acquireMutex(); fileLRU.acquireMutex(); streamLRU.acquireMutex(); @@ -255,10 +271,16 @@ public class ClusterManager { clusterLRU.releaseMutex(); } + private AtomicBoolean rollback = new AtomicBoolean(false); + + boolean rolledback() { + return rollback.get(); + } + public void load() throws IOException { // Main state - mainState = MainState.load(dbFolder); + mainState = MainState.load(dbFolder, t -> rollback.set(true)); lastSessionDirectory = dbFolder.resolve(Integer.toString(mainState.headDir - 1)); @@ -292,73 +314,74 @@ public class ClusterManager { throw new IOException("Could not load HeadState due to corruption", e); } } - - workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir)); - Files.createDirectories(workingDirectory); - - csLRU = new LRU("Change Set", workingDirectory); - streamLRU = new LRU("Cluster Stream", workingDirectory); - clusterLRU = new ClusterLRU(this, "Cluster", workingDirectory); - fileLRU = new LRU("External Value", workingDirectory); - - acquireAll(); - - // Clusters - for (String clusterKey : state.clusters) { - String[] parts1 = clusterKey.split("#"); - String[] parts = parts1[0].split("\\."); - long first = new BigInteger(parts[0], 16).longValue(); - long second = new BigInteger(parts[1], 16).longValue(); - ClusterUID uuid = ClusterUID.make(first, second); - Path readDir = dbFolder.resolve(parts1[1]); - int offset = Integer.parseInt(parts1[2]); - int length = Integer.parseInt(parts1[3]); - clusterLRU.map(new ClusterInfo(this, clusterLRU, readDir, uuid, offset, length)); - } - // Files - for (String fileKey : state.files) { -// System.err.println("loadFile: " + fileKey); - String[] parts = fileKey.split("#"); - Path readDir = dbFolder.resolve(parts[1]); - int offset = Integer.parseInt(parts[2]); - int length = Integer.parseInt(parts[3]); - FileInfo info = new FileInfo(fileLRU, readDir, parts[0], offset, length); - fileLRU.map(info); - } - // Update chunks - for (String fileKey : state.stream) { -// System.err.println("loadStream: " + fileKey); - String[] parts = fileKey.split("#"); - Path readDir = dbFolder.resolve(parts[1]); - int offset = Integer.parseInt(parts[2]); - int length = Integer.parseInt(parts[3]); - ClusterStreamChunk info = new ClusterStreamChunk(this, - streamLRU, readDir, parts[0], offset, length); - streamLRU.map(info); - } - // Change sets - for (String fileKey : state.cs) { - String[] parts = fileKey.split("#"); - Path readDir = dbFolder.resolve(parts[1]); - Long revisionId = Long.parseLong(parts[0]); - int offset = Integer.parseInt(parts[2]); - int length = Integer.parseInt(parts[3]); - ChangeSetInfo info = new ChangeSetInfo(csLRU, readDir, revisionId, offset, length); - csLRU.map(info); + try { + workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir)); + Files.createDirectories(workingDirectory); + + csLRU = new LRU(this, "Change Set", workingDirectory); + streamLRU = new LRU(this, "Cluster Stream", workingDirectory); + clusterLRU = new ClusterLRU(this, "Cluster", workingDirectory); + fileLRU = new LRU(this, "External Value", workingDirectory); + + acquireAll(); + + // Clusters + for (String clusterKey : state.clusters) { + String[] parts1 = clusterKey.split("#"); + String[] parts = parts1[0].split("\\."); + long first = new BigInteger(parts[0], 16).longValue(); + long second = new BigInteger(parts[1], 16).longValue(); + ClusterUID uuid = ClusterUID.make(first, second); + Path readDir = dbFolder.resolve(parts1[1]); + int offset = Integer.parseInt(parts1[2]); + int length = Integer.parseInt(parts1[3]); + clusterLRU.map(new ClusterInfo(this, clusterLRU, readDir, uuid, offset, length)); + } + // Files + for (String fileKey : state.files) { + // System.err.println("loadFile: " + fileKey); + String[] parts = fileKey.split("#"); + Path readDir = dbFolder.resolve(parts[1]); + int offset = Integer.parseInt(parts[2]); + int length = Integer.parseInt(parts[3]); + FileInfo info = new FileInfo(fileLRU, readDir, parts[0], offset, length); + fileLRU.map(info); + } + // Update chunks + for (String fileKey : state.stream) { + // System.err.println("loadStream: " + fileKey); + String[] parts = fileKey.split("#"); + Path readDir = dbFolder.resolve(parts[1]); + int offset = Integer.parseInt(parts[2]); + int length = Integer.parseInt(parts[3]); + ClusterStreamChunk info = new ClusterStreamChunk(this, + streamLRU, readDir, parts[0], offset, length); + streamLRU.map(info); + } + // Change sets + for (String fileKey : state.cs) { + String[] parts = fileKey.split("#"); + Path readDir = dbFolder.resolve(parts[1]); + Long revisionId = Long.parseLong(parts[0]); + int offset = Integer.parseInt(parts[2]); + int length = Integer.parseInt(parts[3]); + ChangeSetInfo info = new ChangeSetInfo(csLRU, readDir, revisionId, offset, length); + csLRU.map(info); + } + + releaseAll(); + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + // ROLLBACK ONE DIR UNTIL WE ARE FINE! + throw new IOException(e); } - - releaseAll(); - } - public T clone(ClusterUID uid, ClusterCreator creator) - throws DatabaseException { + public T clone(ClusterUID uid, ClusterCreator creator) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException, IOException { clusterLRU.ensureUpdates(uid); ClusterInfo info = clusterLRU.getWithoutMutex(uid); return info.clone(uid, creator); - } //private int loadCounter = 0; @@ -375,38 +398,32 @@ public class ClusterManager { static Map tasks = new HashMap(); - public void update(ClusterUID uid, ClusterImpl clu) { - + public void update(ClusterUID uid, ClusterImpl clu) throws AcornAccessVerificationException, IllegalAcornStateException { ClusterInfo info = clusterLRU.getWithoutMutex(uid); info.acquireMutex(); try { info.update(clu); - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { info.releaseMutex(); } - } public long getClusterIdOrCreate(ClusterUID clusterUID) { return 1; } - public int getResourceKey(ClusterUID uid, int index) { + public int getResourceKey(ClusterUID uid, int index) throws AcornAccessVerificationException { return clusterLRU.getResourceKey(uid, index); } - public int getResourceKeyWitoutMutex(ClusterUID uid, int index) { + public int getResourceKeyWitoutMutex(ClusterUID uid, int index) throws IllegalAcornStateException { return clusterLRU.getResourceKeyWithoutMutex(uid, index); } - public ClusterIds getClusterIds() throws ProCoreException { - + public ClusterIds getClusterIds() throws IllegalAcornStateException { clusterLRU.acquireMutex(); try { - Collection infos = clusterLRU.values(); final int status = infos.size(); final long[] firsts = new long[status]; @@ -439,61 +456,54 @@ public class ClusterManager { }; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { clusterLRU.releaseMutex(); } - } - public void addIntoCurrentChangeSet(String ccs) { - + public void addIntoCurrentChangeSet(String ccs) throws IllegalAcornStateException { csLRU.acquireMutex(); try { - currentChanges.add(ccs); - } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { - csLRU.releaseMutex(); - } - } - public void commitChangeSet(long changeSetId, byte[] data) { + public void commitChangeSet(long changeSetId, byte[] data) throws IllegalAcornStateException { csLRU.acquireMutex(); try { ArrayList csids = new ArrayList(currentChanges); currentChanges = new ArrayList(); new ChangeSetInfo(csLRU, changeSetId, data, csids); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { csLRU.releaseMutex(); } } - public byte[] getMetadata(long changeSetId) { + public byte[] getMetadata(long changeSetId) throws AcornAccessVerificationException, IllegalAcornStateException { ChangeSetInfo info = csLRU.getWithoutMutex(changeSetId); if (info == null) return null; - info.acquireMutex(); - try { - return info.getMetadataBytes(); - } catch (Throwable t) { - throw new IllegalStateException(t); + info.acquireMutex(); + try { + return info.getMetadataBytes(); + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw e; + } catch (Throwable t) { + throw new IllegalAcornStateException(t); } finally { info.releaseMutex(); } - } - public byte[] getResourceFile(final byte[] clusterUID, - final int resourceIndex) throws ProCoreException { + public byte[] getResourceFile(final byte[] clusterUID, final int resourceIndex) throws AcornAccessVerificationException, IllegalAcornStateException { ClusterUID uid = ClusterUID.make(clusterUID, 0); String key = uid.toString() + "_" + resourceIndex; @@ -502,18 +512,16 @@ public class ClusterManager { info.acquireMutex(); try { return info.getResourceFile(); + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { info.releaseMutex(); } - } - public ResourceSegment getResourceSegment(final byte[] clusterUID, - final int resourceIndex, final long segmentOffset, short segmentSize) - throws ProCoreException { - + public ResourceSegment getResourceSegment(final byte[] clusterUID, final int resourceIndex, final long segmentOffset, short segmentSize) throws AcornAccessVerificationException, IllegalAcornStateException { ClusterUID uid = ClusterUID.make(clusterUID, 0); String key = uid.toString() + "_" + resourceIndex; @@ -523,55 +531,40 @@ public class ClusterManager { try { return info.getResourceSegment(clusterUID, resourceIndex, segmentOffset, segmentSize); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { info.releaseMutex(); } - } - public void modiFileEx(ClusterUID uid, int resourceKey, long offset, - long size, byte[] bytes, long pos, ClusterSupport support) { - + public void modiFileEx(ClusterUID uid, int resourceKey, long offset, long size, byte[] bytes, long pos, ClusterSupport support) throws IllegalAcornStateException { try { - - String key = uid.toString() - + "_" - + ClusterTraits - .getResourceIndexFromResourceKey(resourceKey); + String key = uid.toString() + "_" + ClusterTraits.getResourceIndexFromResourceKey(resourceKey); FileInfo info = null; - fileLRU.acquireMutex(); - try { - info = fileLRU.get(key); - if (info == null) + if (info == null) { info = new FileInfo(fileLRU, key, (int) (offset + size)); - - + } } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { - fileLRU.releaseMutex(); - } info.acquireMutex(); try { info.updateData(bytes, offset, pos, size); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { info.releaseMutex(); } - } catch (DatabaseException e) { e.printStackTrace(); } - } public void shutdown() { @@ -581,4 +574,9 @@ public class ClusterManager { csLRU.shutdown(); } + public void notSafeToMakeSnapshot(IllegalAcornStateException t) { + this.safeToMakeSnapshot.compareAndSet(true, false); + this.cause = t; + } + } diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/FileIO.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/FileIO.java index aa7173285..c5480d86e 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/FileIO.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/FileIO.java @@ -66,15 +66,17 @@ public class FileIO { ByteBuffer bb = ByteBuffer.wrap(bytes, 0, length); try (FileChannel fc = FileChannel.open(path, options, NO_ATTRIBUTES)) { fc.write(bb); + + writePosition += length; + if(TRACE_PERF) { + long duration = System.nanoTime()-start; + double ds = 1e-9*duration; + System.err.println("Wrote " + bytes.length + " bytes @ " + 1e-6*bytes.length / ds + "MB/s"); + } + return result; + } catch (Throwable t) { + throw new IOException("An error occured file saving bytes for file " + path.toAbsolutePath().toString(), t); } - - writePosition += length; - if(TRACE_PERF) { - long duration = System.nanoTime()-start; - double ds = 1e-9*duration; - System.err.println("Wrote " + bytes.length + " bytes @ " + 1e-6*bytes.length / ds + "MB/s"); - } - return result; } public synchronized byte[] readBytes(int offset, int length) throws IOException { diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java index 774b60554..05f9c8de0 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java @@ -21,6 +21,8 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.internal.ClusterChange; import org.simantics.acorn.internal.ClusterUpdateProcessorBase; import org.simantics.acorn.internal.UndoClusterUpdateProcessor; @@ -37,6 +39,7 @@ import org.simantics.db.exception.SDBException; import org.simantics.db.server.ProCoreException; import org.simantics.db.service.ClusterSetsSupport; import org.simantics.db.service.ClusterUID; +import org.simantics.db.service.LifecycleSupport; import org.simantics.utils.datastructures.Pair; import org.simantics.utils.logging.TimeLogger; @@ -52,7 +55,6 @@ public class GraphClientImpl2 implements Database.Session { private ExecutorService executor = Executors.newSingleThreadExecutor(new ClientThreadFactory("Core Main Program", false)); private ExecutorService saver = Executors.newSingleThreadExecutor(new ClientThreadFactory("Core Snapshot Saver", true)); - private static GraphClientImpl2 INSTANCE; private Path dbFolder; private final Database database; private ServiceLocator locator; @@ -83,10 +85,9 @@ public class GraphClientImpl2 implements Database.Session { this.clusters = new ClusterManager(dbFolder); load(); ClusterSetsSupport cssi = locator.getService(ClusterSetsSupport.class); - cssi.updateReadAndWriteDirectories(clusters.lastSessionDirectory, clusters.workingDirectory); + cssi.setReadDirectory(clusters.lastSessionDirectory); mainProgram = new MainProgram(this, clusters); executor.execute(mainProgram); - INSTANCE = this; } public Path getDbFolder() { @@ -95,7 +96,7 @@ public class GraphClientImpl2 implements Database.Session { public void tryMakeSnapshot() throws IOException { - if (isClosing) + if (isClosing || unexpectedClose) return; saver.execute(new Runnable() { @@ -120,32 +121,42 @@ public class GraphClientImpl2 implements Database.Session { } finally { mainProgram.mutex.release(); } - } catch (IOException e) { - Logger.defaultLogError(e); - } catch (ProCoreException e) { + } catch (IllegalAcornStateException | ProCoreException e) { Logger.defaultLogError(e); + unexpectedClose = true; } catch (InterruptedException e) { Logger.defaultLogError(e); } finally { try { if(tr != null) endTransaction(tr.getTransactionId()); + if (unexpectedClose) { + LifecycleSupport support = getServiceLocator().getService(LifecycleSupport.class); + try { + support.close(); + } catch (DatabaseException e1) { + Logger.defaultLogError(e1); + } + } } catch (ProCoreException e) { Logger.defaultLogError(e); } } } - }); } - public void makeSnapshot(boolean force) throws IOException { - if (safeToMakeSnapshot) - clusters.makeSnapshot(locator, force); + public void makeSnapshot(boolean fullSave) throws IllegalAcornStateException { + clusters.makeSnapshot(locator, fullSave); } public T clone(ClusterUID uid, ClusterCreator creator) throws DatabaseException { - return clusters.clone(uid, creator); + try { + return clusters.clone(uid, creator); + } catch (AcornAccessVerificationException | IllegalAcornStateException | IOException e) { + unexpectedClose = true; + throw new DatabaseException(e); + } } // private void save() throws IOException { @@ -167,8 +178,7 @@ public class GraphClientImpl2 implements Database.Session { private boolean closed = false; private boolean isClosing = false; - // Add check to make sure if it safe to make snapshot (used with cancel which is not yet supported and may cause corrupted head.state writing) - private boolean safeToMakeSnapshot = true; + private boolean unexpectedClose = false; @Override public void close() throws ProCoreException { @@ -176,7 +186,8 @@ public class GraphClientImpl2 implements Database.Session { if(!closed && !isClosing) { isClosing = true; try { - makeSnapshot(true); + if (!unexpectedClose) + makeSnapshot(true); mainProgram.close(); clusters.shutdown(); @@ -187,16 +198,15 @@ public class GraphClientImpl2 implements Database.Session { System.err.println("executorTerminated=" + executorTerminated + ", saverTerminated=" + saverTerminated); - INSTANCE = null; mainProgram = null; executor = null; saver = null; - } catch (IOException | InterruptedException e) { + } catch (IllegalAcornStateException | InterruptedException e) { throw new ProCoreException(e); } + closed = true; } - closed = true; //impl.close(); } @@ -212,27 +222,26 @@ public class GraphClientImpl2 implements Database.Session { @Override public void acceptCommit(long transactionId, long changeSetId, byte[] metadata) throws ProCoreException { - clusters.state.headChangeSetId++; - long committedChangeSetId = changeSetId + 1; - - clusters.commitChangeSet(committedChangeSetId, metadata); - - clusters.state.transactionId = transactionId; - - mainProgram.committed(); - - TimeLogger.log("Accepted commit"); - + try { + clusters.commitChangeSet(committedChangeSetId, metadata); + + clusters.state.transactionId = transactionId; + + mainProgram.committed(); + + TimeLogger.log("Accepted commit"); + } catch (IllegalAcornStateException e) { + throw new ProCoreException(e); + } } @Override - public long cancelCommit(long transactionId, long changeSetId, - byte[] metadata, OnChangeSetUpdate onChangeSetUpdate) - throws ProCoreException { - safeToMakeSnapshot = false; - throw new UnsupportedOperationException("org.simantics.acorn.GraphClientImpl2.cancelCommit() is not supported operation! Closing down to prevent further havoc"); + public long cancelCommit(long transactionId, long changeSetId, byte[] metadata, OnChangeSetUpdate onChangeSetUpdate) throws ProCoreException { + UnsupportedOperationException e = new UnsupportedOperationException("org.simantics.acorn.GraphClientImpl2.cancelCommit() is not supported operation! Closing down to prevent further havoc"); + clusters.notSafeToMakeSnapshot(new IllegalAcornStateException(e)); + throw e; // System.err.println("GraphClientImpl2.cancelCommit() called!! this is experimental and might cause havoc!"); // try { // undo(new long[] {changeSetId}, onChangeSetUpdate); @@ -366,23 +375,18 @@ public class GraphClientImpl2 implements Database.Session { * This method cannot be synchronized since it waits and must support multiple entries * by query thread(s) and internal transactions such as snapshot saver */ - public Transaction askWriteTransaction() - throws ProCoreException { + public Transaction askWriteTransaction() throws IllegalAcornStateException { Semaphore semaphore = new Semaphore(0); - TransactionRequest req = queue(TransactionState.WRITE, semaphore); try { semaphore.acquire(); } catch (InterruptedException e) { - throw new ProCoreException(e); + throw new IllegalAcornStateException(e); } - mainProgram.startTransaction(clusters.state.headChangeSetId+1); - return makeTransaction(req); - } public synchronized long endTransaction(long transactionId) throws ProCoreException { @@ -404,9 +408,15 @@ public class GraphClientImpl2 implements Database.Session { } @Override - public Transaction askWriteTransaction(final long transactionId) - throws ProCoreException { - return transactionManager.askWriteTransaction(); + public Transaction askWriteTransaction(final long transactionId) throws ProCoreException { + try { + if (isClosing || unexpectedClose || closed) { + throw new ProCoreException("GraphClientImpl2 is already closing so no more write transactions allowed!"); + } + return transactionManager.askWriteTransaction(); + } catch (IllegalAcornStateException e) { + throw new ProCoreException(e); + } } @Override @@ -422,9 +432,12 @@ public class GraphClientImpl2 implements Database.Session { } @Override - public byte[] getChangeSetMetadata(long changeSetId) - throws ProCoreException { - return clusters.getMetadata(changeSetId); + public byte[] getChangeSetMetadata(long changeSetId) throws ProCoreException { + try { + return clusters.getMetadata(changeSetId); + } catch (AcornAccessVerificationException | IllegalAcornStateException e) { + throw new ProCoreException(e); + } } @Override @@ -455,7 +468,11 @@ public class GraphClientImpl2 implements Database.Session { @Override public ClusterIds getClusterIds() throws ProCoreException { - return clusters.getClusterIds(); + try { + return clusters.getClusterIds(); + } catch (IllegalAcornStateException e) { + throw new ProCoreException(e); + } } @Override @@ -511,16 +528,17 @@ public class GraphClientImpl2 implements Database.Session { } - public byte[] getResourceFile(final byte[] clusterUID, final int resourceIndex) throws ProCoreException { + public byte[] getResourceFile(final byte[] clusterUID, final int resourceIndex) throws ProCoreException, AcornAccessVerificationException, IllegalAcornStateException { return clusters.getResourceFile(clusterUID, resourceIndex); } @Override - public ResourceSegment getResourceSegment(final byte[] clusterUID, - final int resourceIndex, final long segmentOffset, short segmentSize) throws ProCoreException { - - return clusters.getResourceSegment(clusterUID, resourceIndex, segmentOffset, segmentSize); - + public ResourceSegment getResourceSegment(final byte[] clusterUID, final int resourceIndex, final long segmentOffset, short segmentSize) throws ProCoreException { + try { + return clusters.getResourceSegment(clusterUID, resourceIndex, segmentOffset, segmentSize); + } catch (AcornAccessVerificationException | IllegalAcornStateException e) { + throw new ProCoreException(e); + } } @Override @@ -530,42 +548,43 @@ public class GraphClientImpl2 implements Database.Session { @Override public void updateCluster(byte[] operations) throws ProCoreException { - - ClusterUpdateOperation operation = new ClusterUpdateOperation(clusters, operations); - ClusterInfo info = clusters.clusterLRU.getOrCreate(operation.uid, true); - if(info == null) throw new IllegalStateException(); - info.acquireMutex(); - try { + ClusterInfo info = null; + try { + ClusterUpdateOperation operation = new ClusterUpdateOperation(clusters, operations); + info = clusters.clusterLRU.getOrCreate(operation.uid, true); + if(info == null) + throw new IllegalAcornStateException("info == null for operation " + operation); + info.acquireMutex(); info.scheduleUpdate(); mainProgram.schedule(operation); - } catch (Throwable t) { - throw new IllegalStateException(t); - } finally { - info.releaseMutex(); + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw new ProCoreException(e); + } finally { + if (info != null) + info.releaseMutex(); } - } - private UndoClusterUpdateProcessor getUndoCSS(String ccsId) throws DatabaseException { + private UndoClusterUpdateProcessor getUndoCSS(String ccsId) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { String[] ss = ccsId.split("\\."); String chunkKey = ss[0]; int chunkOffset = Integer.parseInt(ss[1]); ClusterStreamChunk chunk = clusters.streamLRU.getWithoutMutex(chunkKey); - if(chunk == null) throw new IllegalStateException("Cluster Stream Chunk " + chunkKey + " was not found."); + if(chunk == null) throw new IllegalAcornStateException("Cluster Stream Chunk " + chunkKey + " was not found."); chunk.acquireMutex(); try { return chunk.getUndoProcessor(clusters, chunkOffset, ccsId); + } catch (DatabaseException e) { + throw e; } catch (Throwable t) { throw new IllegalStateException(t); } finally { chunk.releaseMutex(); } - } - private void performUndo(String ccsId, ArrayList> clusterChanges, UndoClusterSupport support) throws ProCoreException, DatabaseException { - + private void performUndo(String ccsId, ArrayList> clusterChanges, UndoClusterSupport support) throws ProCoreException, DatabaseException, IllegalAcornStateException, AcornAccessVerificationException { UndoClusterUpdateProcessor proc = getUndoCSS(ccsId); int clusterKey = clusters.getClusterKeyByClusterUIDOrMakeWithoutMutex(proc.getClusterUID()); @@ -578,103 +597,96 @@ public class GraphClientImpl2 implements Database.Session { Entry e = proc.entries.get(proc.entries.size() - 1 - i); e.process(clusters, cs, clusterKey); - } - cs.flush(); } finally { clusters.clusterLRU.releaseMutex(); } - } @Override public boolean undo(long[] changeSetIds, OnChangeSetUpdate onChangeSetUpdate) throws SDBException { - - final ArrayList> clusterChanges = new ArrayList>(); - - UndoClusterSupport support = new UndoClusterSupport(clusters); - - final int changeSetId = clusters.state.headChangeSetId; - - if(ClusterUpdateProcessorBase.DEBUG) - System.err.println(" === BEGIN UNDO ==="); - - for(int i=0;i ccss = clusters.getChanges(id); - for(int j=0;j pair = clusterChanges.get(i); - - final ClusterUID cuid = pair.first; - final byte[] data = pair.second; - - onChangeSetUpdate.onChangeSetUpdate(new ChangeSetUpdate() { - - @Override - public long getChangeSetId() { - return changeSetId; - } - - @Override - public int getChangeSetIndex() { - return 0; - } - - @Override - public int getNumberOfClusterChangeSets() { - return clusterChanges.size(); - } - - @Override - public int getIndexOfClusterChangeSet() { - return changeSetIndex; - } - - @Override - public byte[] getClusterId() { - return cuid.asBytes(); - } - - @Override - public boolean getNewCluster() { - return false; - } - - @Override - public byte[] getData() { - return data; - } - - }); - - } - - + try { + final ArrayList> clusterChanges = new ArrayList>(); + + UndoClusterSupport support = new UndoClusterSupport(clusters); + + final int changeSetId = clusters.state.headChangeSetId; + + if(ClusterUpdateProcessorBase.DEBUG) + System.err.println(" === BEGIN UNDO ==="); + + for(int i=0;i ccss = clusters.getChanges(id); + + for(int j=0;j pair = clusterChanges.get(i); + + final ClusterUID cuid = pair.first; + final byte[] data = pair.second; + + onChangeSetUpdate.onChangeSetUpdate(new ChangeSetUpdate() { + + @Override + public long getChangeSetId() { + return changeSetId; + } + + @Override + public int getChangeSetIndex() { + return 0; + } + + @Override + public int getNumberOfClusterChangeSets() { + return clusterChanges.size(); + } + + @Override + public int getIndexOfClusterChangeSet() { + return changeSetIndex; + } + + @Override + public byte[] getClusterId() { + return cuid.asBytes(); + } + + @Override + public boolean getNewCluster() { + return false; + } + + @Override + public byte[] getData() { + return data; + } + + }); + } + } catch (AcornAccessVerificationException | IllegalAcornStateException e1) { + throw new ProCoreException(e1); + } return false; - - } - - public static GraphClientImpl2 getInstance() { - return INSTANCE; } public ServiceLocator getServiceLocator() { @@ -686,6 +698,11 @@ public class GraphClientImpl2 implements Database.Session { return false; } + @Override + public boolean rolledback() { + return clusters.rolledback(); + } + diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/HeadState.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/HeadState.java index c20d8e878..dd8703c1f 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/HeadState.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/HeadState.java @@ -1,7 +1,6 @@ package org.simantics.acorn; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; @@ -11,6 +10,7 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; +import org.simantics.acorn.exception.InvalidHeadStateException; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.serialization.Serializer; @@ -18,6 +18,9 @@ import org.simantics.databoard.util.binary.BinaryMemory; public class HeadState { + public static final String HEAD_STATE = "head.state"; + public static final String SHA_1 = "SHA-1"; + public int headChangeSetId = 0; public long transactionId = 1; public long reservedIds = 3; @@ -29,11 +32,11 @@ public class HeadState { // public ArrayList ccs = new ArrayList(); public static HeadState load(Path directory) throws InvalidHeadStateException { - Path f = directory.resolve("head.state"); + Path f = directory.resolve(HEAD_STATE); try { byte[] bytes = Files.readAllBytes(f); - MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + MessageDigest sha1 = MessageDigest.getInstance(SHA_1); int digestLength = sha1.getDigestLength(); sha1.update(bytes, digestLength, bytes.length - digestLength); @@ -43,10 +46,10 @@ public class HeadState { "Checksum " + Arrays.toString(newChecksum) + " does not match excpected " + Arrays.toString(Arrays.copyOfRange(bytes, 0, digestLength)) + " for " + f.toAbsolutePath()); } - - HeadState object = (HeadState) org.simantics.databoard.Files.readFile(new ByteArrayInputStream(bytes, digestLength, bytes.length - digestLength), Bindings.getBindingUnchecked(HeadState.class)); - return object; - + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes, digestLength, bytes.length - digestLength)) { + HeadState object = (HeadState) org.simantics.databoard.Files.readFile(bais, Bindings.getBindingUnchecked(HeadState.class)); + return object; + } } catch (IOException i) { return new HeadState(); // throw new InvalidHeadStateException(i); @@ -58,7 +61,7 @@ public class HeadState { } public void save(Path directory) throws IOException { - Path f = directory.resolve("head.state"); + Path f = directory.resolve(HEAD_STATE); try { BinaryMemory rf = new BinaryMemory(4096); try { @@ -71,7 +74,7 @@ public class HeadState { byte[] bytes = rf.toByteBuffer().array(); - MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + MessageDigest sha1 = MessageDigest.getInstance(SHA_1); sha1.update(bytes); byte[] checksum = sha1.digest(); @@ -88,7 +91,7 @@ public class HeadState { public static void validateHeadStateIntegrity(Path headState) throws InvalidHeadStateException, IOException { try { byte[] bytes = Files.readAllBytes(headState); - MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + MessageDigest sha1 = MessageDigest.getInstance(SHA_1); int digestLength = sha1.getDigestLength(); sha1.update(bytes, digestLength, bytes.length - digestLength); byte[] newChecksum = sha1.digest(); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/MainProgram.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/MainProgram.java index f39a4987d..78ff9e899 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/MainProgram.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/MainProgram.java @@ -7,12 +7,15 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.lru.ClusterStreamChunk; import org.simantics.acorn.lru.ClusterUpdateOperation; import org.simantics.db.service.ClusterUID; @@ -167,9 +170,7 @@ public class MainProgram implements Runnable, Closeable { } catch (InterruptedException e) { e.printStackTrace(); } - } - } // long sss = System.nanoTime(); @@ -191,19 +192,27 @@ public class MainProgram implements Runnable, Closeable { final List ops = updateSchedules[i]; if (!ops.isEmpty()) { acquireAmount++; - clusterUpdateThreads[i].execute(() -> { - - //long st = System.nanoTime(); - for(ClusterUpdateOperation op : ops) { - op.run(); - } - s.release(); - // long duration = System.nanoTime()-st; - // elapsed.addAndGet(duration); - // double dur = 1e-9*duration; - // if(dur > 0.05) - // System.err.println("duration=" + dur + "s. " + ops.size()); - }); + clusterUpdateThreads[i].submit(new Callable() { + + @Override + public Object call() throws Exception { + //long st = System.nanoTime(); + try { + for(ClusterUpdateOperation op : ops) { + op.run(); + } + } finally { + s.release(); + } + return null; + + // long duration = System.nanoTime()-st; + // elapsed.addAndGet(duration); + // double dur = 1e-9*duration; + // if(dur > 0.05) + // System.err.println("duration=" + dur + "s. " + ops.size()); + } + }); } } @@ -217,8 +226,8 @@ public class MainProgram implements Runnable, Closeable { clusters.streamLRU.acquireMutex(); try { swapChunks(); - } catch (Throwable t) { - throw new IllegalStateException(t); + } catch (AcornAccessVerificationException | IllegalAcornStateException e) { + e.printStackTrace(); } finally { clusters.streamLRU.releaseMutex(); } @@ -226,7 +235,7 @@ public class MainProgram implements Runnable, Closeable { try { swapCS(); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { clusters.csLRU.releaseMutex(); } @@ -240,31 +249,28 @@ public class MainProgram implements Runnable, Closeable { } finally { deathBarrier.release(); } - } /* * Mutex for streamLRU is assumed here * */ - private void swapChunks() { + private void swapChunks() throws AcornAccessVerificationException, IllegalAcornStateException { // Cache chunks during update operations boolean written = clusters.streamLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE); while(written) { written = clusters.streamLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE); } - } - private void swapCS() { + private void swapCS() throws AcornAccessVerificationException, IllegalAcornStateException { // Cache chunks during update operations boolean written = clusters.csLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE); while(written) { written = clusters.csLRU.swap(Integer.MAX_VALUE, CHUNK_CACHE_SIZE); } - } public synchronized void committed() { @@ -278,7 +284,7 @@ public class MainProgram implements Runnable, Closeable { } - public synchronized void schedule(ClusterUpdateOperation operation) { + public synchronized void schedule(ClusterUpdateOperation operation) throws IllegalAcornStateException { if (!alive) { System.err.println("Trying to schedule operation after MainProgram is closed! Operation is " + operation); // return; @@ -303,15 +309,13 @@ public class MainProgram implements Runnable, Closeable { swapChunks(); notifyAll(); - + } catch (IllegalAcornStateException e) { + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { - clusters.streamLRU.releaseMutex(); - } - } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java index 77335289d..ec8451cca 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java @@ -1,69 +1,96 @@ package org.simantics.acorn; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; +import java.io.OutputStream; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.simantics.acorn.exception.InvalidHeadStateException; +import org.simantics.databoard.Bindings; +import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.file.RuntimeIOException; +import org.simantics.databoard.serialization.Serializer; +import org.simantics.databoard.util.binary.BinaryMemory; import org.simantics.utils.FileUtils; public class MainState implements Serializable { private static final long serialVersionUID = 6237383147637270225L; + public static final String MAIN_STATE = "main.state"; + public int headDir = 0; public MainState() { } - - public MainState(int headDir) { + + private MainState(int headDir) { this.headDir = headDir; } - public static MainState load(Path directory) throws IOException { + public static MainState load(Path directory, Consumer callback) throws IOException { Files.createDirectories(directory); - Path f = directory.resolve("main.state"); + Path mainState = directory.resolve(MAIN_STATE); try { + byte[] bytes = Files.readAllBytes(mainState); MainState state = null; - try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(f)))) { - state = (MainState) in.readObject(); + try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes)) { + state = (MainState) org.simantics.databoard.Files.readFile(bais, Bindings.getBindingUnchecked(MainState.class)); } + while (true) { - Path last = directory.resolve(Integer.toString(state.headDir - 1)); + Path latest = directory.resolve(Integer.toString(state.headDir - 1)); try { - Path headState = last.resolve("head.state"); + Path headState = latest.resolve(HeadState.HEAD_STATE); HeadState.validateHeadStateIntegrity(headState); break; } catch (InvalidHeadStateException e) { e.printStackTrace(); state.headDir--; - uncheckedDeleteAll(last); + callback.accept(e); + } finally { + cleanBaseDirectory(directory, latest, callback); } } return state; - } catch(IOException i) { - return new MainState( findNewHeadState(directory) ); - } catch(ClassNotFoundException c) { - throw new Error("MainState class not found", c); + } catch(Exception i) { + callback.accept(i); + int largest = -1; + Path latest = findNewHeadStateDir(directory, callback); + if (latest != null) + largest = safeParseInt(-1, latest.getFileName().toString()); + // +1 because we want to return the next head version to use, + // not the latest existing version. + largest++; + MainState state = new MainState( largest ); + cleanBaseDirectory(directory, latest, callback); + return state; } finally { - if (Files.exists(f)) { - Files.delete(f); + if (Files.exists(mainState)) { + Files.delete(mainState); } } } public void save(Path directory) throws IOException { - Path f = directory.resolve("main.state"); - try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(Files.newOutputStream(f)))) { - out.writeObject(this); + Path f = directory.resolve(MAIN_STATE); + BinaryMemory rf = new BinaryMemory(4096); + try { + MutableVariant v = new MutableVariant(Bindings.getBindingUnchecked(MainState.class), this); + Serializer s = Bindings.getSerializerUnchecked( Bindings.VARIANT ); + s.serialize(rf, v); + } finally { + rf.close(); + } + byte[] bytes = rf.toByteBuffer().array(); + try (OutputStream out = Files.newOutputStream(f)) { + out.write(bytes); } FileIO.syncPath(f); } @@ -78,13 +105,13 @@ public class MainState implements Serializable { } /** - * TODO> shouldn't do two things in the same function, this does both head.state search and directory cleanup * * @param directory + * @param callback * @return * @throws IOException */ - private static int findNewHeadState(Path directory) throws IOException { + private static Path findNewHeadStateDir(Path directory, Consumer callback) throws IOException { try (Stream s = Files.walk(directory, 1)) { List reverseSortedPaths = s .filter(p -> !p.equals(directory) && isInteger(p) && Files.isDirectory(p)) @@ -94,25 +121,19 @@ public class MainState implements Serializable { return Integer.compare(p2Name, p1Name); }).collect(Collectors.toList()); - int largest = -1; + Path latest = null; for (Path last : reverseSortedPaths) { - Path headState = last.resolve("head.state"); - if (Files.exists(headState)) { - try { - HeadState.validateHeadStateIntegrity(headState); - largest = safeParseInt(-1, last.getFileName().toString()); - break; - } catch (IOException | InvalidHeadStateException e) { - e.printStackTrace(); - uncheckedDeleteAll(last); - } - } else { - uncheckedDeleteAll(last); + Path headState = last.resolve(HeadState.HEAD_STATE); + try { + HeadState.validateHeadStateIntegrity(headState); + latest = last; + break; + } catch (IOException | InvalidHeadStateException e) { + // Cleanup is done in {@link cleanBaseDirectory} method + callback.accept(e); } } - // +1 because we want to return the next head version to use, - // not the latest existing version. - return largest + 1; + return latest; } } @@ -124,6 +145,30 @@ public class MainState implements Serializable { } } + private static void cleanBaseDirectory(Path directory, Path latest, Consumer callback) throws IOException { + try (Stream s = Files.walk(directory, 1)) { + List reverseSortedPaths = s + .filter(p -> !p.equals(directory) && isInteger(p) && Files.isDirectory(p)) + .sorted((p1, p2) -> { + int p1Name = Integer.parseInt(p1.getFileName().toString()); + int p2Name = Integer.parseInt(p2.getFileName().toString()); + return Integer.compare(p2Name, p1Name); + }).collect(Collectors.toList()); + + for (Path p : reverseSortedPaths) { + if (!p.equals(latest)) { + // this indicates that there is a possibility that index and vg's are out of sync + // if we are able to find folders with higher number than the current head.state + callback.accept(null); + uncheckedDeleteAll(p); + } else { + break; + } + } + + } + } + private static void uncheckedDeleteAll(Path path) { try { FileUtils.deleteAll(path.toFile()); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/Persistable.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/Persistable.java index 0d209b2b8..86dfdd435 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/Persistable.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/Persistable.java @@ -3,9 +3,13 @@ package org.simantics.acorn; import java.io.IOException; import java.nio.file.Path; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; +import org.simantics.db.exception.SDBException; + public interface Persistable { void toFile(Path path) throws IOException ; - void fromFile(byte[] data); + void fromFile(byte[] data) throws IllegalAcornStateException, AcornAccessVerificationException; } \ No newline at end of file diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/backup/AcornBackupProvider.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/backup/AcornBackupProvider.java index 5ea0799d8..3977ad73d 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/backup/AcornBackupProvider.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/backup/AcornBackupProvider.java @@ -16,7 +16,9 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.simantics.acorn.AcornSessionManagerImpl; import org.simantics.acorn.GraphClientImpl2; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.backup.BackupException; import org.simantics.backup.IBackupProvider; import org.simantics.db.server.ProCoreException; @@ -32,7 +34,12 @@ public class AcornBackupProvider implements IBackupProvider { private static final String IDENTIFIER = "AcornBackupProvider"; private long trId = -1; private final Semaphore lock = new Semaphore(1); + private final GraphClientImpl2 client; + public AcornBackupProvider() { + this.client = AcornSessionManagerImpl.getInstance().getClient(); + } + private static Path getAcornMetadataFile(Path dbFolder) { return dbFolder.getParent().resolve(IDENTIFIER); } @@ -42,8 +49,7 @@ public class AcornBackupProvider implements IBackupProvider { try { if (trId != -1) throw new IllegalStateException(this + " backup provider is already locked"); - trId = GraphClientImpl2.getInstance().askWriteTransaction(-1) - .getTransactionId(); + trId = client.askWriteTransaction(-1).getTransactionId(); } catch (ProCoreException e) { e.printStackTrace(); } @@ -55,7 +61,6 @@ public class AcornBackupProvider implements IBackupProvider { try { lock.acquire(); - GraphClientImpl2 client = GraphClientImpl2.getInstance(); client.makeSnapshot(true); Path dbDir = client.getDbFolder(); @@ -79,7 +84,7 @@ public class AcornBackupProvider implements IBackupProvider { throw new BackupException("Failed to lock Acorn for backup.", e); } catch (NumberFormatException e) { throw new BackupException("Failed to read Acorn head state file.", e); - } catch (IOException e) { + } catch (IllegalAcornStateException | IOException e) { throw new BackupException("I/O problem during Acorn backup.", e); } finally { if (releaseLock) @@ -92,7 +97,7 @@ public class AcornBackupProvider implements IBackupProvider { try { if (trId == -1) throw new BackupException(this + " backup provider is not locked"); - GraphClientImpl2.getInstance().endTransaction(trId); + client.endTransaction(trId); trId = -1; } catch (ProCoreException e) { throw new BackupException(e); @@ -105,7 +110,7 @@ public class AcornBackupProvider implements IBackupProvider { // 1. Resolve initial backup restore target. // This can be DB directory directly or a temporary directory that // will replace the DB directory. - Path dbRoot = GraphClientImpl2.getInstance().getDbFolder(); + Path dbRoot = client.getDbFolder(); Path restorePath = dbRoot; if (!Files.exists(dbRoot, LinkOption.NOFOLLOW_LINKS)) { Files.createDirectories(dbRoot); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterBig.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterBig.java index f623d587f..51241728d 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterBig.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterBig.java @@ -16,6 +16,8 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.internal.ClusterChange; import org.simantics.acorn.internal.ClusterStream; import org.simantics.acorn.internal.ClusterSupport2; @@ -571,8 +573,11 @@ final public class ClusterBig extends ClusterImpl { try { return resourceTable.getValue(valueTable, resourceIndex); } catch (ExternalValueException e) { - return clusterSupport.impl.getResourceFile(clusterUID.asBytes(), resourceIndex); -// return support.getValueEx(resourceIndex, clusterUID.second); + try { + return clusterSupport.impl.getResourceFile(clusterUID.asBytes(), resourceIndex); + } catch (AcornAccessVerificationException | IllegalAcornStateException e1) { + throw new DatabaseException(e1); + } } } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterSmall.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterSmall.java index 726071dbe..b84d4d51f 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterSmall.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/cluster/ClusterSmall.java @@ -16,15 +16,16 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.internal.ClusterChange; import org.simantics.acorn.internal.ClusterStream; import org.simantics.acorn.internal.ClusterSupport2; import org.simantics.acorn.internal.DebugPolicy; import org.simantics.db.Resource; +import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ExternalValueException; import org.simantics.db.exception.ValidationException; -import org.simantics.db.impl.ClusterBase; import org.simantics.db.impl.ClusterI; import org.simantics.db.impl.ClusterSupport; import org.simantics.db.impl.ClusterTraitsBase; @@ -708,8 +709,7 @@ final public class ClusterSmall extends ClusterImpl { } } @Override - public byte[] getValue(int resourceKey, ClusterSupport support) - throws DatabaseException { + public byte[] getValue(int resourceKey, ClusterSupport support) throws DatabaseException { if (DEBUG) System.out.println("ClusterSmall.getValue " + resourceKey); int resourceIndex = ClusterTraitsBase.getResourceIndexFromResourceKeyNoThrow(resourceKey); @@ -838,12 +838,11 @@ final public class ClusterSmall extends ClusterImpl { return resourceTable.getUsedSize(); } - public int getNumberOfResources() { - - if(proxy) throw new IllegalStateException(); + public int getNumberOfResources() throws IllegalAcornStateException { + if(proxy) + throw new IllegalAcornStateException("proxy == true for " + clusterId); return resourceTable.getUsedSize(); - } @Override @@ -1134,7 +1133,13 @@ final public class ClusterSmall extends ClusterImpl { }); return "ClusterSmall[" + getClusterUID() + " - " + getClusterId() + " - " + getNumberOfResources() + " - " + foreignTable.getResourceHashMap().size() + " - " + set.size() + "]"; } catch (DatabaseException e) { - return "ClusterSmall[" + getNumberOfResources() + "]"; + try { + return "ClusterSmall[" + getNumberOfResources() + "]"; + } catch (IllegalAcornStateException e1) { + Logger.defaultLogError(e1); + e1.printStackTrace(); + return "An exception occured!!"; + } } } diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/AcornAccessVerificationException.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/AcornAccessVerificationException.java new file mode 100644 index 000000000..6269a60b3 --- /dev/null +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/AcornAccessVerificationException.java @@ -0,0 +1,20 @@ +package org.simantics.acorn.exception; + +import org.simantics.db.exception.SDBException; + +public class AcornAccessVerificationException extends SDBException { + + private static final long serialVersionUID = 6601855907356895356L; + + public AcornAccessVerificationException(String message, Throwable cause) { + super(message, cause); + } + + public AcornAccessVerificationException(String message) { + super(message); + } + + public AcornAccessVerificationException(Throwable cause) { + super(cause); + } +} diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/IllegalAcornStateException.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/IllegalAcornStateException.java new file mode 100644 index 000000000..8228d59c1 --- /dev/null +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/IllegalAcornStateException.java @@ -0,0 +1,21 @@ +package org.simantics.acorn.exception; + +import org.simantics.db.exception.SDBException; + +public class IllegalAcornStateException extends SDBException { + + private static final long serialVersionUID = -8255505454138490120L; + + public IllegalAcornStateException(String message, Throwable cause) { + super(message, cause); + } + + public IllegalAcornStateException(String message) { + super(message); + } + + public IllegalAcornStateException(Throwable cause) { + super(cause); + } + +} diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/InvalidHeadStateException.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/InvalidHeadStateException.java similarity index 94% rename from bundles/org.simantics.acorn/src/org/simantics/acorn/InvalidHeadStateException.java rename to bundles/org.simantics.acorn/src/org/simantics/acorn/exception/InvalidHeadStateException.java index 2c342b71f..7c8510399 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/InvalidHeadStateException.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/exception/InvalidHeadStateException.java @@ -1,4 +1,4 @@ -package org.simantics.acorn; +package org.simantics.acorn.exception; public class InvalidHeadStateException extends Exception { diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterSupport2.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterSupport2.java index 7cd007ace..b1b1e8365 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterSupport2.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterSupport2.java @@ -8,6 +8,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.simantics.acorn.ClusterManager; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.db.Session; import org.simantics.db.exception.DatabaseException; import org.simantics.db.impl.ClusterBase; @@ -82,7 +84,11 @@ public class ClusterSupport2 implements ClusterSupport, IClusterTable { @Override public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) { - return impl.getClusterKeyByClusterUIDOrMakeWithoutMutex(clusterUID); + try { + return impl.getClusterKeyByClusterUIDOrMakeWithoutMutex(clusterUID); + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw new RuntimeException(e); + } } @Override @@ -185,14 +191,16 @@ public class ClusterSupport2 implements ClusterSupport, IClusterTable { } } - public ResourceSegment getResourceSegment(int resourceIndex, ClusterUID clusterUID, long offset, short size) - throws DatabaseException { + public ResourceSegment getResourceSegment(int resourceIndex, ClusterUID clusterUID, long offset, short size) throws DatabaseException { if (DEBUG) System.out.println("DEBUG: getResourceSegment ri=" + resourceIndex + " cid=" + clusterUID + " offset=" + offset + " size=" + size); - org.simantics.db.Database.Session.ResourceSegment t = impl.getResourceSegment(clusterUID.asBytes(), resourceIndex, offset, size); - return new ResourceSegment(t.getValueSize(), t.getSegment()); - + try { + org.simantics.db.Database.Session.ResourceSegment t = impl.getResourceSegment(clusterUID.asBytes(), resourceIndex, offset, size); + return new ResourceSegment(t.getValueSize(), t.getSegment()); + } catch (AcornAccessVerificationException | IllegalAcornStateException e) { + throw new DatabaseException(e); + } } protected byte[] getValueBig(ClusterBase cluster, int resourceIndex, int offset, int length) throws DatabaseException { @@ -202,8 +210,11 @@ public class ClusterSupport2 implements ClusterSupport, IClusterTable { ClusterUID clusterUID = cluster.clusterUID; - return impl.getResourceFile(clusterUID.asBytes(), resourceIndex); - + try { + return impl.getResourceFile(clusterUID.asBytes(), resourceIndex); + } catch (AcornAccessVerificationException | IllegalAcornStateException e) { + throw new DatabaseException(e); + } } protected InputStream getValueStreamBig(ClusterBase cluster, final int resourceIndex, int offset, int length) throws DatabaseException { @@ -247,7 +258,8 @@ public class ClusterSupport2 implements ClusterSupport, IClusterTable { @Override public int read() throws IOException { - if(left <= 0) throw new IllegalStateException(); + if(left <= 0) + throw new IOException("left <= 0 for " + _s); if(offset == _s.bytes.length) { short slen = (short)Math.min(left, IMAX); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor.java index 0044d72d8..d4381aeca 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor.java @@ -2,7 +2,7 @@ package org.simantics.acorn.internal; import org.simantics.acorn.ClusterManager; import org.simantics.acorn.cluster.ClusterImpl; -import org.simantics.acorn.lru.CachingClusterSupport; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.lru.ClusterUpdateOperation; import org.simantics.db.exception.DatabaseException; import org.simantics.db.impl.ClusterSupport; @@ -76,7 +76,7 @@ public class ClusterUpdateProcessor extends ClusterUpdateProcessorBase { } - public ClusterImpl process(ClusterImpl cluster) { + public ClusterImpl process(ClusterImpl cluster) throws IllegalAcornStateException { this.cluster = cluster; process(); info.finish(); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor2.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor2.java index 7ce8673cb..61a8a8a9d 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor2.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessor2.java @@ -1,6 +1,7 @@ package org.simantics.acorn.internal; import org.simantics.acorn.cluster.ClusterImpl; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.lru.ClusterUpdateOperation; import org.simantics.db.impl.ClusterSupport; @@ -16,7 +17,7 @@ public class ClusterUpdateProcessor2 extends ClusterUpdateProcessorBase2 { this.info = info; } - public void process(ClusterImpl cluster) { + public void process(ClusterImpl cluster) throws IllegalAcornStateException { this.cluster = cluster; process(); info.finish(); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase.java index e0e733c1e..cd8130d9c 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import org.simantics.acorn.ClusterManager; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.internal.ClusterStream.ClusterEnum; import org.simantics.acorn.internal.ClusterStream.Data; import org.simantics.acorn.internal.ClusterStream.StmEnum; @@ -26,7 +27,7 @@ abstract public class ClusterUpdateProcessorBase { final Map clusterKeyCache = new HashMap(); - public int getResourceKey(ClusterUID uid, int index) { + public int getResourceKey(ClusterUID uid, int index) throws IllegalAcornStateException { Integer match = clusterKeyCache.get(uid); if(match != null) return match+index; int key = manager.getResourceKeyWitoutMutex(uid, 0); @@ -192,7 +193,7 @@ abstract public class ClusterUpdateProcessorBase { int lows[] = new int[2]; int foreignRefs[] = new int[2]; - private void processStatement(int op, StmEnum stmEnum, ClusterEnum p, ClusterEnum o) { + private void processStatement(int op, StmEnum stmEnum, ClusterEnum p, ClusterEnum o) throws IllegalAcornStateException { int curPos = pos-1-24; @@ -307,7 +308,7 @@ abstract public class ClusterUpdateProcessorBase { } - public void process() { + public void process() throws IllegalAcornStateException { foreignPos = 0; diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase2.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase2.java index e821b46eb..502729c0b 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase2.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/ClusterUpdateProcessorBase2.java @@ -1,5 +1,6 @@ package org.simantics.acorn.internal; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.db.service.Bytes; import org.simantics.db.service.ClusterUID; @@ -35,7 +36,7 @@ public abstract class ClusterUpdateProcessorBase2 { pos+=4; } - public void process() { + public void process() throws IllegalAcornStateException { while(pos < len) { @@ -50,12 +51,10 @@ public abstract class ClusterUpdateProcessorBase2 { processUndoValue(op); break; default: - throw new IllegalStateException(); + throw new IllegalAcornStateException("Can not process cluster " + uid); } - } - } abstract void setImmutable(boolean value); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/UndoClusterUpdateProcessor.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/UndoClusterUpdateProcessor.java index d545e51ad..8b3e4f066 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/UndoClusterUpdateProcessor.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/UndoClusterUpdateProcessor.java @@ -5,6 +5,8 @@ import java.util.Arrays; import java.util.List; import org.simantics.acorn.ClusterManager; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.lru.ClusterChangeSet; import org.simantics.acorn.lru.ClusterStreamChunk; import org.simantics.acorn.lru.ClusterChangeSet.Entry; @@ -28,7 +30,7 @@ public class UndoClusterUpdateProcessor extends ClusterUpdateProcessorBase { this.ccs = ccs; } - private static byte[] readOperation(ClusterManager manager, ClusterStreamChunk chunk, ClusterChangeSet ccs) { + private static byte[] readOperation(ClusterManager manager, ClusterStreamChunk chunk, ClusterChangeSet ccs) throws AcornAccessVerificationException, IllegalAcornStateException { // ClusterStreamChunk chunk; // manager.streamLRU.acquireMutex(); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ChangeSetInfo.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ChangeSetInfo.java index 12351a519..a730e136d 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ChangeSetInfo.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ChangeSetInfo.java @@ -4,6 +4,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.db.service.Bytes; import org.simantics.utils.datastructures.Pair; @@ -15,13 +17,13 @@ public class ChangeSetInfo extends LRUObject { private ArrayList clusterChangeSetIds; // Stub - public ChangeSetInfo(LRU LRU, Path readDir, Long revision, int offset, int length) { + public ChangeSetInfo(LRU LRU, Path readDir, Long revision, int offset, int length) throws AcornAccessVerificationException { super(LRU, revision, readDir, "clusterStream", offset, length, false, false); LRU.map(this); } // New - public ChangeSetInfo(LRU LRU, Long revision, byte[] bytes, ArrayList clusterChangeSetIds) { + public ChangeSetInfo(LRU LRU, Long revision, byte[] bytes, ArrayList clusterChangeSetIds) throws AcornAccessVerificationException { super(LRU, revision, LRU.getDirectory(), "clusterStream", true, true); this.metadataBytes = bytes; this.metadataBytes = bytes; @@ -29,19 +31,17 @@ public class ChangeSetInfo extends LRUObject { LRU.insert(this, accessTime); } - public ArrayList getCSSIds() { + public ArrayList getCSSIds() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); return clusterChangeSetIds; } - public byte[] getMetadataBytes() { - - if(VERIFY) verifyAccess(); + public byte[] getMetadataBytes() throws AcornAccessVerificationException, IllegalAcornStateException { + if(VERIFY) + verifyAccess(); makeResident(); - return metadataBytes; - } private static void writeLE(TByteArrayList bytes, int value) { diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterChangeSet.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterChangeSet.java index d0f3013d0..cfe5c1357 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterChangeSet.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterChangeSet.java @@ -3,6 +3,8 @@ package org.simantics.acorn.lru; import java.util.ArrayList; import org.simantics.acorn.ClusterManager; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.internal.Change; import org.simantics.acorn.internal.ClusterChange; import org.simantics.db.procore.cluster.ClusterTraits; @@ -40,9 +42,9 @@ public class ClusterChangeSet { this.newValue = null; } - public Entry(int subject, boolean oldValueEx, byte[] oldValue, byte[] newValue) { + public Entry(int subject, boolean oldValueEx, byte[] oldValue, byte[] newValue) throws IllegalAcornStateException { if(oldValue == null && newValue == null) - throw new IllegalStateException(); + throw new IllegalAcornStateException("oldValue == null && newValue == null"); this.type = Type.VALUE; this.subject = (short)(subject & 0xFFF); this.predicate = 0; @@ -54,7 +56,7 @@ public class ClusterChangeSet { this.newValue = newValue; } - public void process(ClusterManager clusters, ClusterChange cs, int clusterKey) { + public void process(ClusterManager clusters, ClusterChange cs, int clusterKey) throws AcornAccessVerificationException { Entry e = this; @@ -113,7 +115,7 @@ public class ClusterChangeSet { chunkOffset = Integer.parseInt(ss[1]); } - public ClusterStreamChunk getChunk(ClusterManager manager) { + public ClusterStreamChunk getChunk(ClusterManager manager) throws AcornAccessVerificationException { return manager.streamLRU.get(chunkKey); } diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterInfo.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterInfo.java index 1cd582267..57dfe9f41 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterInfo.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterInfo.java @@ -8,12 +8,15 @@ import org.simantics.acorn.ClusterManager; import org.simantics.acorn.Persistable; import org.simantics.acorn.cluster.ClusterImpl; import org.simantics.acorn.cluster.ClusterSmall; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.cluster.ClusterImpl.ClusterTables; import org.simantics.acorn.internal.ClusterSupport2; import org.simantics.compressions.CompressionCodec; import org.simantics.compressions.Compressions; import org.simantics.db.ClusterCreator; import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.SDBException; import org.simantics.db.service.Bytes; import org.simantics.db.service.ClusterUID; import org.simantics.utils.datastructures.Pair; @@ -28,7 +31,7 @@ public class ClusterInfo extends LRUObject implements P public static final String COMPRESSION = "LZ4"; // Stub - public ClusterInfo(ClusterManager manager, LRU LRU, Path readDirectory, ClusterUID uid, int offset, int length) { + public ClusterInfo(ClusterManager manager, LRU LRU, Path readDirectory, ClusterUID uid, int offset, int length) throws AcornAccessVerificationException { super(LRU, uid, readDirectory, uid.toString() + ".cluster", offset, length, false, false); this.manager = manager; this.cluster = null; @@ -36,7 +39,7 @@ public class ClusterInfo extends LRUObject implements P } // New - public ClusterInfo(ClusterManager manager, LRU LRU, ClusterImpl cluster) { + public ClusterInfo(ClusterManager manager, LRU LRU, ClusterImpl cluster) throws AcornAccessVerificationException, IllegalAcornStateException { super(LRU, cluster.getClusterUID(), LRU.getDirectory(), cluster.getClusterUID().toString() + ".cluster", true, true); this.manager = manager; this.cluster = cluster; @@ -44,7 +47,7 @@ public class ClusterInfo extends LRUObject implements P LRU.swap(getKey()); } - public T clone(ClusterUID uid, ClusterCreator creator) throws DatabaseException { + public T clone(ClusterUID uid, ClusterCreator creator) throws IOException, AcornAccessVerificationException, IllegalAcornStateException { // Updates have been ensured at this point @@ -56,9 +59,9 @@ public class ClusterInfo extends LRUObject implements P return creator.create(uid, tables.bytes, tables.ints, tables.longs); } } catch (IOException e) { - throw new DatabaseException(e); + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { releaseMutex(); } @@ -80,9 +83,9 @@ public class ClusterInfo extends LRUObject implements P } } catch (IOException e) { - throw new DatabaseException(e); + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { releaseMutex(); } @@ -170,10 +173,8 @@ public class ClusterInfo extends LRUObject implements P } @Override - protected Pair toBytes() { - + protected Pair toBytes() throws IllegalAcornStateException { try { - byte[] raw = null; if(cluster instanceof ClusterSmall) { @@ -215,7 +216,7 @@ public class ClusterInfo extends LRUObject implements P return Pair.make(result, compressedSize+4); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { release(); } @@ -233,7 +234,7 @@ public class ClusterInfo extends LRUObject implements P return "cluster"; } - public void scheduleUpdate() { + public void scheduleUpdate() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -243,10 +244,8 @@ public class ClusterInfo extends LRUObject implements P } - public ClusterImpl getForUpdate() { - + public ClusterImpl getForUpdate() throws SDBException { try { - acquireMutex(); assert(updateState != null); @@ -255,18 +254,16 @@ public class ClusterInfo extends LRUObject implements P setDirty(true); updateState.beginUpdate(); return cluster; - + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { - releaseMutex(); - } - } - public void update(ClusterImpl clu) { + public void update(ClusterImpl clu) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -278,7 +275,7 @@ public class ClusterInfo extends LRUObject implements P } - public ClusterImpl getCluster() { + public ClusterImpl getCluster() throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -289,7 +286,7 @@ public class ClusterInfo extends LRUObject implements P } @Override - public boolean canBePersisted() { + public boolean canBePersisted() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -302,7 +299,7 @@ public class ClusterInfo extends LRUObject implements P } - private ClusterUpdateState getUpdateState() { + private ClusterUpdateState getUpdateState() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -310,13 +307,13 @@ public class ClusterInfo extends LRUObject implements P } - private ClusterUpdateState getUpdateStateWithoutMutex() { + private ClusterUpdateState getUpdateStateWithoutMutex() throws IllegalAcornStateException { try { acquireMutex(); return getUpdateState(); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { releaseMutex(); } @@ -326,7 +323,7 @@ public class ClusterInfo extends LRUObject implements P /* * This method blocks - no locks here */ - public void waitForUpdates() { + public void waitForUpdates() throws IllegalAcornStateException { ClusterUpdateState state = getUpdateStateWithoutMutex(); if(state != null) { @@ -335,7 +332,6 @@ public class ClusterInfo extends LRUObject implements P long duration = System.nanoTime() - start; System.err.println("Wait updates to cluster " + getKey() + " for " + (1e-6 * duration) + "ms."); } - } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterLRU.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterLRU.java index 1f0db5435..6b5252161 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterLRU.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterLRU.java @@ -6,6 +6,8 @@ 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; @@ -19,18 +21,14 @@ import gnu.trove.TIntIntHashMap; public class ClusterLRU extends LRU { final private BijectionMap clusterMapping = new BijectionMap(); - final private ClusterManager manager; public ClusterLRU(ClusterManager manager, String identifier, Path writeDir) { - - super(identifier, writeDir); - this.manager = manager; + super(manager, identifier, writeDir); clusterMapping.map(ClusterUID.make(0,2), clusterMapping.size() + 1); - } - public ClusterInfo getOrCreate(ClusterUID uid, boolean makeIfNull) { + public ClusterInfo getOrCreate(ClusterUID uid, boolean makeIfNull) throws IllegalAcornStateException, AcornAccessVerificationException { try { @@ -40,7 +38,7 @@ public class ClusterLRU extends LRU { if (info == null) { - if(!makeIfNull) throw new IllegalStateException("Asked for an existing cluster " + uid + " that was not found."); + if(!makeIfNull) throw new IllegalAcornStateException("Asked for an existing cluster " + uid + " that was not found."); Integer clusterKey = clusterMapping.getRight(uid); if (clusterKey == null) { @@ -54,9 +52,10 @@ public class ClusterLRU extends LRU { } return info; - + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { releaseMutex(); @@ -68,16 +67,15 @@ public class ClusterLRU extends LRU { /* * This method waits - we have no locks here */ - public void ensureUpdates(ClusterUID uid) throws DatabaseException { + 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 DatabaseException { + public ClusterInfo get(ClusterUID uid, boolean makeIfNull, boolean ensureUpdates) throws AcornAccessVerificationException, IllegalAcornStateException { if (ensureUpdates) { try { @@ -86,19 +84,18 @@ public class ClusterLRU extends LRU { if (makeIfNull) { Logger.defaultLogError("For debug purposes, creating cluster which does not exist", e); } else { - throw e; + throw new IllegalAcornStateException(e); } } } return getOrCreate(uid, makeIfNull); } - public ClusterInfo get(ClusterUID uid, boolean makeIfNull) throws DatabaseException { + public ClusterInfo get(ClusterUID uid, boolean makeIfNull) throws AcornAccessVerificationException, IllegalAcornStateException { return get(uid, makeIfNull, true); - } - public int getResourceKey(ClusterUID uid, int index) { + public int getResourceKey(ClusterUID uid, int index) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -111,20 +108,19 @@ public class ClusterLRU extends LRU { } - public int getResourceKeyWithoutMutex(ClusterUID uid, int index) { + public int getResourceKeyWithoutMutex(ClusterUID uid, int index) throws IllegalAcornStateException { acquireMutex(); try { return getResourceKey(uid, index); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { releaseMutex(); } - } - public int createClusterKeyByClusterUID(ClusterUID uid) { + public int createClusterKeyByClusterUID(ClusterUID uid) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -137,7 +133,7 @@ public class ClusterLRU extends LRU { } - public ClusterBase getClusterByClusterUIDOrMake(ClusterUID uid) throws DatabaseException { + public ClusterBase getClusterByClusterUIDOrMake(ClusterUID uid) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -146,7 +142,7 @@ public class ClusterLRU extends LRU { } - public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) { + public int getClusterKeyByClusterUIDOrMake(ClusterUID clusterUID) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -154,18 +150,20 @@ public class ClusterLRU extends LRU { } - public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) { + public int getClusterKeyByClusterUIDOrMakeWithoutMutex(ClusterUID clusterUID) throws IllegalAcornStateException, AcornAccessVerificationException { acquireMutex(); try { return getClusterKeyByClusterUIDOrMake(clusterUID); + } catch (AcornAccessVerificationException e) { + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { releaseMutex(); } } - public ClusterBase getClusterByClusterKey(int clusterKey) throws DatabaseException { + public ClusterBase getClusterByClusterKey(int clusterKey) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -174,16 +172,16 @@ public class ClusterLRU extends LRU { info.acquireMutex(); try { return info.getCluster(); + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { info.releaseMutex(); } - } - public ClusterUID getClusterUIDByResourceKey(int resourceKey) - throws DatabaseException { + public ClusterUID getClusterUIDByResourceKey(int resourceKey) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -192,24 +190,22 @@ public class ClusterLRU extends LRU { } - public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws DatabaseException { + public ClusterUID getClusterUIDByResourceKeyWithoutMutex(int resourceKey) throws IllegalAcornStateException, AcornAccessVerificationException { acquireMutex(); try { return getClusterUIDByResourceKey(resourceKey); - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { releaseMutex(); } } @SuppressWarnings("unchecked") - public T getClusterByClusterUIDOrMakeProxy(ClusterUID uid) throws DatabaseException { + public T getClusterByClusterUIDOrMakeProxy(ClusterUID uid) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { return (T) getClusterByClusterUIDOrMake(uid); } @SuppressWarnings("unchecked") - public T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException { + public T getClusterProxyByResourceKey(int resourceKey) throws DatabaseException, AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -217,7 +213,7 @@ public class ClusterLRU extends LRU { } - public int getClusterKeyByUID(long id1, long id2) throws DatabaseException { + public int getClusterKeyByUID(long id1, long id2) throws DatabaseException, AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -225,17 +221,15 @@ public class ClusterLRU extends LRU { } - public int getClusterKeyByUIDWithoutMutex(long id1, long id2) throws DatabaseException { - + public int getClusterKeyByUIDWithoutMutex(long id1, long id2) throws DatabaseException, IllegalAcornStateException { acquireMutex(); try { return getClusterKeyByClusterUIDOrMake(ClusterUID.make(id1, id2)); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { releaseMutex(); } - } diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterStreamChunk.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterStreamChunk.java index 23cbfb1ce..57d6f6a04 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterStreamChunk.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterStreamChunk.java @@ -7,6 +7,8 @@ import java.util.ArrayList; import org.simantics.acorn.ClusterManager; import org.simantics.acorn.Persistable; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.acorn.internal.ClusterChange; import org.simantics.acorn.internal.UndoClusterUpdateProcessor; import org.simantics.compressions.CompressionCodec; @@ -31,14 +33,14 @@ public class ClusterStreamChunk extends LRUObject im public ArrayList operations = new ArrayList(); // Stub - public ClusterStreamChunk(ClusterManager manager, LRU LRU, Path readDir, String id, int offset, int length) { + public ClusterStreamChunk(ClusterManager manager, LRU LRU, Path readDir, String id, int offset, int length) throws AcornAccessVerificationException { super(LRU, id, readDir, "clusterStream", offset, length, false, false); this.manager = manager; LRU.map(this); } // Creation - public ClusterStreamChunk(ClusterManager manager, LRU LRU, String id) { + public ClusterStreamChunk(ClusterManager manager, LRU LRU, String id) throws AcornAccessVerificationException { super(LRU, id, LRU.getDirectory(), "clusterStream", true, true); this.manager = manager; LRU.insert(this, accessTime); @@ -51,8 +53,8 @@ public class ClusterStreamChunk extends LRUObject im makeResident(true); ClusterUpdateOperation op = operations.get(chunkOffset); - if(op == null) throw new IllegalStateException("Cluster Update Operation " + ccsId + " was not found."); - if(op.ccs == null) throw new IllegalStateException("Cluster ChangeSet " + ccsId + " was not found."); + if(op == null) throw new IllegalAcornStateException("Cluster Update Operation " + ccsId + " was not found."); + if(op.ccs == null) throw new IllegalAcornStateException("Cluster ChangeSet " + ccsId + " was not found."); UndoClusterUpdateProcessor proc = new UndoClusterUpdateProcessor(clusters, this, op.ccs); if(proc.version != ClusterChange.VERSION) @@ -69,9 +71,9 @@ public class ClusterStreamChunk extends LRUObject im } - public void addOperation(ClusterUpdateOperation op) { + public void addOperation(ClusterUpdateOperation op) throws IllegalAcornStateException { if(committed) - throw new IllegalStateException(); + throw new IllegalAcornStateException("Cannot add operation " + op + " to " + this + " if commited == true"); operations.add(op); size += op.data.length; // if(isCommitted()) { @@ -93,7 +95,7 @@ public class ClusterStreamChunk extends LRUObject im } @Override - public boolean canBePersisted() { + public boolean canBePersisted() throws AcornAccessVerificationException { if(!super.canBePersisted()) return false; if(!isCommitted()) return false; for(ClusterUpdateOperation op : operations) { @@ -213,7 +215,7 @@ public class ClusterStreamChunk extends LRUObject im private static StreamDecompressor decompressor = new StreamDecompressor(); @Override - public void fromFile(byte[] data_) { + public void fromFile(byte[] data_) throws IllegalAcornStateException, AcornAccessVerificationException { try { @@ -271,17 +273,13 @@ public class ClusterStreamChunk extends LRUObject im } } - operations.add(op); - } - } catch (IOException e) { - - throw new IllegalStateException(e); - - } - + throw new IllegalAcornStateException(e); + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw e; + } } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterUpdateOperation.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterUpdateOperation.java index 40a44bc43..c23821b79 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterUpdateOperation.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/ClusterUpdateOperation.java @@ -2,6 +2,8 @@ package org.simantics.acorn.lru; 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.ClusterChange; import org.simantics.acorn.internal.ClusterChange2; import org.simantics.acorn.internal.ClusterUpdateProcessor; @@ -21,7 +23,7 @@ final public class ClusterUpdateOperation { public ClusterChangeSet ccs; boolean finished = false; - public ClusterUpdateOperation(ClusterManager manager, byte[] data) { + public ClusterUpdateOperation(ClusterManager manager, byte[] data) throws IllegalAcornStateException, AcornAccessVerificationException { long cuid1 = Bytes.readLE8(data, 8); long cuid2 = Bytes.readLE8(data, 16); @@ -30,20 +32,19 @@ final public class ClusterUpdateOperation { this.uid = ClusterUID.make(cuid1, cuid2); this.data = data; this.info = manager.clusterLRU.getOrCreate(uid, true); - } public void finish() { finished = true; } - public void scheduled(String ccsInfoId) { + public void scheduled(String ccsInfoId) throws AcornAccessVerificationException, IllegalAcornStateException { ccs = new ClusterChangeSet(ccsInfoId, uid); chunk = ccs.getChunk(manager); manager.addIntoCurrentChangeSet(ccsInfoId); } - public void run() { + public void run() throws AcornAccessVerificationException, IllegalAcornStateException { ClusterUpdateOperation op = null; byte[] data = null; chunk.acquireMutex(); @@ -51,15 +52,13 @@ final public class ClusterUpdateOperation { chunk.makeResident(); op = chunk.operations.get(ccs.chunkOffset); data = op.data; - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { chunk.releaseMutex(); } op.runWithData(data); } - public void runWithData(byte[] data) { + public void runWithData(byte[] data) throws IllegalAcornStateException, AcornAccessVerificationException { try { int version = Bytes.readLE4(data, 4); @@ -74,12 +73,13 @@ final public class ClusterUpdateOperation { processor.process(cluster); manager.update(uid, cluster); } else { - throw new IllegalStateException(); + throw new IllegalAcornStateException("unsupported clusterChange version " + version); } + } catch (IllegalAcornStateException | AcornAccessVerificationException e) { + throw e; } catch (Throwable t) { - t.printStackTrace(); + throw new IllegalAcornStateException(t); } - } @Override diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/FileInfo.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/FileInfo.java index 660f24501..116464727 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/FileInfo.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/FileInfo.java @@ -2,8 +2,9 @@ package org.simantics.acorn.lru; import java.nio.file.Path; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.db.Database.Session.ResourceSegment; -import org.simantics.db.server.ProCoreException; import org.simantics.utils.datastructures.Pair; import gnu.trove.list.array.TByteArrayList; @@ -13,38 +14,33 @@ public class FileInfo extends LRUObject { private TByteArrayList bytes; // Stub - public FileInfo(LRU LRU, Path readDir, String id, int offset, int length) { + public FileInfo(LRU LRU, Path readDir, String id, int offset, int length) throws AcornAccessVerificationException { super(LRU, id, readDir, id.toString() + ".extFile", offset, length, false, false); LRU.map(this); } // New - public FileInfo(LRU LRU, String id, int size) { + public FileInfo(LRU LRU, String id, int size) throws AcornAccessVerificationException { super(LRU, id, LRU.getDirectory(), id.toString() + ".extFile", true, true); this.bytes = new TByteArrayList(size); LRU.insert(this, accessTime); } - public byte[] getResourceFile() { + public byte[] getResourceFile() throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); makeResident(); return bytes.toArray(); - } - public ResourceSegment getResourceSegment(final byte[] clusterUID, - final int resourceIndex, final long segmentOffset, short segmentSize) - throws ProCoreException { + public ResourceSegment getResourceSegment(final byte[] clusterUID, final int resourceIndex, final long segmentOffset, short segmentSize) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); makeResident(); - try { - int segSize = segmentSize; if (segSize < 0) segSize += 65536; @@ -52,7 +48,6 @@ public class FileInfo extends LRUObject { segSize = Math.min(65535, bytes.size()); final long valueSize = bytes.size(); - final byte[] segment = bytes.toArray((int) segmentOffset, segSize); return new ResourceSegment() { @@ -82,18 +77,12 @@ public class FileInfo extends LRUObject { return clusterUID; } }; - } catch (Throwable t) { - - t.printStackTrace(); - + throw new IllegalAcornStateException(t); } - - throw new UnsupportedOperationException(); - } - public void updateData(byte[] newBytes, long offset, long pos, long size) { + public void updateData(byte[] newBytes, long offset, long pos, long size) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); makeResident(); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRU.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRU.java index 508127db9..323d66d3d 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRU.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRU.java @@ -12,7 +12,10 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import org.simantics.acorn.ClusterManager; import org.simantics.acorn.GraphClientImpl2; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.db.common.utils.Logger; /* @@ -39,8 +42,11 @@ public class LRU> { private Thread mutexOwner; public Map pending = new HashMap(); + + protected final ClusterManager manager; - public LRU(String identifier, Path writeDir) { + public LRU(ClusterManager manager, String identifier, Path writeDir) { + this.manager = manager; this.identifier = identifier; this.writeDir = writeDir; resume(); @@ -50,19 +56,15 @@ public class LRU> { * Public interface */ - public void acquireMutex() { - + public void acquireMutex() throws IllegalAcornStateException { try { - while(!mutex.tryAcquire(3, TimeUnit.SECONDS)) { System.err.println("Mutex is taking a long time to acquire - owner is " + mutexOwner); } - if(VERIFY) mutexOwner = Thread.currentThread(); - } catch (InterruptedException e) { - throw new IllegalStateException(e); + throw new IllegalAcornStateException(e); } } @@ -90,7 +92,6 @@ public class LRU> { public Thread newThread(Runnable r) { return new Thread(r, identifier + " File Writer"); } - }); if (GraphClientImpl2.DEBUG) System.err.println("Resuming LRU writers " + writers); @@ -100,12 +101,10 @@ public class LRU> { * This method violates the synchronization order rule between LRU and MapVAlue * External synchronization is used to ensure correct operation */ - public void persist(ArrayList state) { + public void persist(ArrayList state) throws IllegalAcornStateException { acquireMutex(); - try { - for (MapValue value : values()) { value.acquireMutex(); // for debugging purposes @@ -124,78 +123,69 @@ public class LRU> { try { // Record the value state.add(value.getStateKey()); - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { value.releaseMutex(); } } - + } catch (IllegalAcornStateException e) { + throw e; + } catch (IOException e) { + throw new IllegalAcornStateException("Unable to waitPending for " + this.identifier, e); } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException("Fatal error occured for " + this.identifier, t); } finally { releaseMutex(); } - } - public MapValue getWithoutMutex(MapKey key) { + public MapValue getWithoutMutex(MapKey key) throws AcornAccessVerificationException, IllegalAcornStateException { acquireMutex(); try { return get(key); - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { releaseMutex(); } - } - public MapValue get(MapKey key) { + public MapValue get(MapKey key) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); return map.get(key); - } - public void map(MapValue info) { + public void map(MapValue info) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); map.put(info.getKey(), info); - } - public Collection values() { + public Collection values() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); return map.values(); - } - public boolean swapForced() { + public boolean swapForced() throws IllegalAcornStateException, AcornAccessVerificationException { acquireMutex(); try { return swap(0, 0, null); - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { releaseMutex(); } } - public boolean swap(long lifeTime, int targetSize) { + public boolean swap(long lifeTime, int targetSize) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); return swap(lifeTime, targetSize, null); - } /* @@ -204,7 +194,6 @@ public class LRU> { public void setWriteDir(Path dir) { this.writeDir = dir; - } @@ -212,19 +201,18 @@ public class LRU> { * Package access */ - void insert(MapValue info, long accessTime) { + void insert(MapValue info, long accessTime) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); map.put(info.getKey(), info); priorityQueue.put(accessTime, info.getKey()); - } /* * We have access to ClusterLRU - try to refresh value if available */ - boolean tryRefresh(MapValue info) { + boolean tryRefresh(MapValue info) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -232,28 +220,20 @@ public class LRU> { return false; try { - priorityQueue.remove(info.getLastAccessTime()); info.accessed(); map.put(info.getKey(), info); priorityQueue.put(info.getLastAccessTime(), info.getKey()); - return true; - - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { - info.releaseMutex(); - } - } /* * We have access to MapValue and no access to clusterLRU */ - void refresh(MapValue info, boolean needMutex) { + void refresh(MapValue info, boolean needMutex) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) { if(!needMutex) verifyAccess(); @@ -270,38 +250,31 @@ public class LRU> { map.put(info.getKey(), info); priorityQueue.put(info.getLastAccessTime(), info.getKey()); + } catch (AcornAccessVerificationException e) { + throw e; } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { - if(needMutex) releaseMutex(); - } - } /* * Private implementation */ - public int size() { - + int size() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); - return priorityQueue.size(); - } - boolean swap(MapKey excluded) { - + boolean swap(MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); - return swap(swapTime, swapSize, excluded); - } - boolean swap(long lifeTime, int targetSize, MapKey excluded) { + boolean swap(long lifeTime, int targetSize, MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -309,29 +282,22 @@ public class LRU> { if(valueToSwap != null) { if(valueToSwap.tryAcquireMutex()) { - try { - if(valueToSwap.canBePersisted()) { valueToSwap.persist(); return true; } - } catch (Throwable t) { - throw new IllegalStateException(t); + throw new IllegalAcornStateException(t); } finally { valueToSwap.releaseMutex(); } } - } - return false; - } - - private MapValue getValueToSwap1(long lifeTime, int targetSize, MapKey excluded) { + private MapValue getValueToSwap1(long lifeTime, int targetSize, MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -347,15 +313,12 @@ public class LRU> { } return map.get(key); - } - return null; - } - private MapValue getValueToSwap(long lifeTime, int targetSize, MapKey excluded) { + private MapValue getValueToSwap(long lifeTime, int targetSize, MapKey excluded) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); @@ -368,29 +331,20 @@ public class LRU> { if(value.tryAcquireMutex()) { try { - // This may lock the object - if(value.canBePersisted()) return value; + if(value.canBePersisted()) + return value; // Insert back the value refresh(value, false); - - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { - value.releaseMutex(); - } - } - } - return null; - } - private long getSwapCandidate(long lifeTime, int targetSize) { + private long getSwapCandidate(long lifeTime, int targetSize) throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); @@ -411,14 +365,13 @@ public class LRU> { * Tries to persist this object. Can fail if the object cannot be persisted at this time. * */ - boolean persist(Object object_) { + boolean persist(Object object_) throws AcornAccessVerificationException { MapValue object = (MapValue)object_; if(VERIFY) object.verifyAccess(); if(object.isDirty()) { - // It is possible that this just became unpersistable. Fail here in this case. if(!object.canBePersisted()) { return false; @@ -447,21 +400,17 @@ public class LRU> { object.release(); object.setResident(false); return false; - } - return false; - } - int makeResident(Object object_, boolean keepResident) { + int makeResident(Object object_, boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException { MapValue object = (MapValue)object_; if(VERIFY) object.verifyAccess(); try { - object.setForceResident(keepResident); if(object.isResident()) { @@ -480,22 +429,13 @@ public class LRU> { try { refresh(object, false); swap(swapTime, swapSize, object.getKey()); - } catch (Throwable t) { - throw new IllegalStateException(t); } finally { releaseMutex(); } - return data.length; - } catch (IOException e) { - - e.printStackTrace(); - + throw new IllegalAcornStateException("Unable to makeResident " + identifier, e); } - - return 0; - } static int readCounter = 0; @@ -503,50 +443,48 @@ public class LRU> { ScheduledThreadPoolExecutor writers; - void waitPending(MapValue value, boolean hasMutex) { + void waitPending(MapValue value, boolean hasMutex) throws IOException, AcornAccessVerificationException, IllegalAcornStateException { - WriteRunnable r = null; + WriteRunnable runnable = null; boolean inProgress = false; synchronized(pending) { - r = pending.get(value.getKey().toString()); - if(r != null) { - synchronized(r) { - if(r.committed) { + runnable = pending.get(value.getKey().toString()); + if(runnable != null) { + synchronized(runnable) { + if(runnable.committed) { // just being written - just need to wait inProgress = true; } else { - r.committed = true; + runnable.committed = true; // we do the writing } } } } - if(r != null) { + if(runnable != null) { if(inProgress) { // System.err.println("reader waits for WriteRunnable to finish"); try { - r.s.acquire(); + if(hasMutex) { + runnable.borrowMutex = true; + } + runnable.s.acquire(); } catch (InterruptedException e) { - e.printStackTrace(); + throw new IllegalAcornStateException(e); } } else { // System.err.println("reader took WriteRunnable"); - try { - r.runReally(hasMutex); - } catch (Throwable e) { - e.printStackTrace(); - Logger.defaultLogError(e); - } + runnable.runReally(hasMutex); } } - } public class WriteRunnable implements Runnable { - Path bytes; - MapValue impl; - boolean committed = false; + private Path bytes; + private MapValue impl; + private boolean committed = false; + private boolean borrowMutex = false; private Semaphore s = new Semaphore(0); WriteRunnable(Path bytes, MapValue impl) { @@ -556,48 +494,77 @@ public class LRU> { @Override public void run() { - synchronized(impl) { - - synchronized(this) { - - if(committed) return; - - committed = true; - - } - try { - runReally(false); - } catch (Throwable e) { - e.printStackTrace(); - Logger.defaultLogError(e); - } - } + try { + synchronized(impl) { + + synchronized(this) { + + if(committed) + return; + + committed = true; + } + runReally(false); + } + } catch (Throwable t) { + if (t instanceof IllegalAcornStateException) { + manager.notSafeToMakeSnapshot((IllegalAcornStateException)t); + } else { + manager.notSafeToMakeSnapshot(new IllegalAcornStateException(t)); + } + t.printStackTrace(); + Logger.defaultLogError(t); + } } - public void runReally(boolean hasMutex) throws IOException { - - if(!hasMutex) - impl.acquireMutex(); - - try { - - // These have been set in method persist - assert(!impl.isResident()); - assert(!impl.isDirty()); - - impl.toFile(bytes); - - synchronized(pending) { - pending.remove(impl.getKey().toString()); - s.release(Integer.MAX_VALUE); - } - } finally { - if(!hasMutex) - impl.releaseMutex(); - } - - } - + public void runWithMutex() throws IOException, IllegalAcornStateException, AcornAccessVerificationException { + + try { + // These have been set in method persist + assert (!impl.isResident()); + assert (!impl.isDirty()); + + impl.toFile(bytes); + } finally { + synchronized (pending) { + pending.remove(impl.getKey().toString()); + s.release(Integer.MAX_VALUE); + } + } + + } + + // Fix WriteRunnable.runReally() to use LRU.MapValue mutex instead of + // borrowMutex + public void runReally(boolean hasMutex) throws IOException, IllegalAcornStateException, AcornAccessVerificationException { + + if (hasMutex) { + + runWithMutex(); + + } else { + + boolean gotMutex = impl.tryAcquireMutex(); + + boolean done = false; + while (!done) { + + if (gotMutex || borrowMutex) { + runWithMutex(); + done = true; + } else { + System.err.println("Retry mutex acquire"); + gotMutex = impl.tryAcquireMutex(); + } + + } + + if (gotMutex) + impl.releaseMutex(); + + } + + } } public Path getDirectory() { @@ -609,10 +576,9 @@ public class LRU> { * */ - protected void verifyAccess() { -// assert (mutex.availablePermits() == 0); + protected void verifyAccess() throws AcornAccessVerificationException { if (mutex.availablePermits() != 0) - throw new IllegalStateException("identifier=" + identifier + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner); + throw new AcornAccessVerificationException("identifier=" + identifier + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner); } /* diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRUObject.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRUObject.java index 1079bf5f0..3194d591e 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRUObject.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/lru/LRUObject.java @@ -7,6 +7,8 @@ import java.util.concurrent.TimeUnit; import org.simantics.acorn.FileIO; import org.simantics.acorn.Persistable; +import org.simantics.acorn.exception.AcornAccessVerificationException; +import org.simantics.acorn.exception.IllegalAcornStateException; import org.simantics.utils.datastructures.Pair; public abstract class LRUObject> implements Persistable { @@ -59,10 +61,8 @@ public abstract class LRUObject pair = toBytes(); - byte[] data = pair.first; - int length = pair.second; - FileIO fio = FileIO.get(bytes); - int offset = fio.saveBytes(data, length, overwrite()); - setPosition(offset, length); - } - - public int makeResident() { + if(VERIFY) { + try { + verifyAccess(); + } catch (AcornAccessVerificationException e) { + throw new IOException("Exception occured during toFile for file " + fileName, e); + } + } + try { + Pair pair = toBytes(); + byte[] data = pair.first; + int length = pair.second; + FileIO fio = FileIO.get(bytes); + int offset = fio.saveBytes(data, length, overwrite()); + setPosition(offset, length); + } catch (AcornAccessVerificationException | IllegalAcornStateException e) { + throw new IOException("Exception occured during toFile for file " + fileName, e); + } + } + + public int makeResident() throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); return LRU.makeResident(this, false); } - public int makeResident(boolean keepResident) { + public int makeResident(boolean keepResident) throws AcornAccessVerificationException, IllegalAcornStateException { if(VERIFY) verifyAccess(); return LRU.makeResident(this, true); } @@ -111,24 +121,24 @@ public abstract class LRUObject toBytes(); + abstract protected Pair toBytes() throws IllegalAcornStateException; - protected void setDirty() { + protected void setDirty() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); dirty = true; } - protected void verifyAccess() { - assert(mutex.availablePermits() == 0); + protected void verifyAccess() throws AcornAccessVerificationException { + if (mutex.availablePermits() != 0) + throw new AcornAccessVerificationException("fileName=" + fileName + " mutex has " + mutex.availablePermits() + " available permits, should be 0! Current mutexOwner is " + mutexOwner); } - protected synchronized void cancelForceResident() { + protected synchronized void cancelForceResident() throws AcornAccessVerificationException { setForceResident(false); } @@ -208,27 +219,27 @@ public abstract class LRUObject 0 for " + fileName + " in " + readDirectory.toAbsolutePath() + ", dirty=" + dirty + ", resident=" + resident + ", forceResident=" + forceResident); } - private Path getDirectory() { + private Path getDirectory() throws AcornAccessVerificationException { if(VERIFY) verifyAccess(); return readDirectory; } diff --git a/bundles/org.simantics.acorn/src/org/simantics/db/javacore/HeadState.java b/bundles/org.simantics.acorn/src/org/simantics/db/javacore/HeadState.java index 5a96be2df..0fb29333b 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/db/javacore/HeadState.java +++ b/bundles/org.simantics.acorn/src/org/simantics/db/javacore/HeadState.java @@ -11,7 +11,7 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import org.simantics.acorn.InvalidHeadStateException; +import org.simantics.acorn.exception.InvalidHeadStateException; public class HeadState implements Serializable {