diff --git a/src/main/java/net/openhft/chronicle/hash/ChronicleHashClosedException.java b/src/main/java/net/openhft/chronicle/hash/ChronicleHashClosedException.java index c80e01694..cdcf7b74b 100644 --- a/src/main/java/net/openhft/chronicle/hash/ChronicleHashClosedException.java +++ b/src/main/java/net/openhft/chronicle/hash/ChronicleHashClosedException.java @@ -23,7 +23,7 @@ package net.openhft.chronicle.hash; public final class ChronicleHashClosedException extends IllegalStateException { private static final long serialVersionUID = 0L; - public ChronicleHashClosedException() { - super("Access to ChronicleHash after close()"); + public ChronicleHashClosedException(ChronicleHash hash) { + super("Access to " + hash.toIdentityString() + " after close()"); } } diff --git a/src/main/java/net/openhft/chronicle/hash/Data.java b/src/main/java/net/openhft/chronicle/hash/Data.java index 973114783..d87ccdb0d 100644 --- a/src/main/java/net/openhft/chronicle/hash/Data.java +++ b/src/main/java/net/openhft/chronicle/hash/Data.java @@ -28,6 +28,9 @@ import net.openhft.chronicle.map.MapQueryContext; import net.openhft.chronicle.set.ChronicleSet; import org.jetbrains.annotations.Nullable; +import java.io.PrintWriter; +import java.io.StringWriter; + import static net.openhft.chronicle.algo.bytes.Access.checkedRandomDataInputAccess; /** @@ -186,9 +189,8 @@ public interface Data { * {@code get()} method throws an exception. */ default String dataToString() { - T object; try { - object = get(); + return get().toString(); } catch (Exception e) { StringBuilder sb = new StringBuilder(); sb.append("failed to deserialize object from data with bytes: ["); @@ -197,9 +199,11 @@ public interface Data { sb.append(bs.printable(off)); } sb.append("], exception: "); - sb.append(e); + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + String exceptionAsString = sw.toString(); + sb.append(exceptionAsString); return sb.toString(); } - return object.toString(); } } diff --git a/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java b/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java index c0df39a35..dea20abc4 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java @@ -20,7 +20,9 @@ package net.openhft.chronicle.hash.impl; import net.openhft.chronicle.algo.bytes.Access; import net.openhft.chronicle.algo.bytes.NativeAccess; import net.openhft.chronicle.algo.locks.VanillaReadWriteUpdateWithWaitsLockingStrategy; +import net.openhft.chronicle.core.Jvm; import net.openhft.chronicle.core.OS; +import net.openhft.chronicle.hash.locks.InterProcessDeadLockException; import java.util.concurrent.TimeUnit; @@ -55,9 +57,10 @@ public final class BigSegmentHeader implements SegmentHeader { */ public static final int LOCK_TIMEOUT_SECONDS = 60; - private static RuntimeException deadLock() { - return new RuntimeException("Failed to acquire the lock in " + LOCK_TIMEOUT_SECONDS + - " seconds.\nPossible reasons:\n" + + private static InterProcessDeadLockException deadLock() { + return new InterProcessDeadLockException( + "Failed to acquire the lock in " + LOCK_TIMEOUT_SECONDS + " seconds.\n" + + "Possible reasons:\n" + " - The lock was not released by the previous holder. If you use contexts API,\n" + " for example map.queryContext(key), in a try-with-resources block.\n" + " - This Chronicle Map (or Set) instance is persisted to disk, and the previous\n" + @@ -188,8 +191,7 @@ public final class BigSegmentHeader implements SegmentHeader { do { if (LOCK.tryReadLock(A, null, address + LOCK_OFFSET)) return true; - if (interruptible && Thread.interrupted()) - throw new InterruptedException(); + checkInterrupted(interruptible); } while (System.nanoTime() <= end); return false; } @@ -203,8 +205,7 @@ public final class BigSegmentHeader implements SegmentHeader { do { if (LOCK.tryReadLock(A, null, address + LOCK_OFFSET)) return true; - if (interruptible && Thread.interrupted()) - throw new InterruptedException(); + checkInterrupted(interruptible); long now = System.currentTimeMillis(); if (now != lastTime) { lastTime = now; @@ -275,8 +276,7 @@ public final class BigSegmentHeader implements SegmentHeader { do { if (LOCK.tryUpdateLock(A, null, address + LOCK_OFFSET)) return true; - if (interruptible && Thread.interrupted()) - throw new InterruptedException(); + checkInterrupted(interruptible); } while (System.nanoTime() <= end); return false; } @@ -290,8 +290,7 @@ public final class BigSegmentHeader implements SegmentHeader { do { if (LOCK.tryUpdateLock(A, null, address + LOCK_OFFSET)) return true; - if (interruptible && Thread.interrupted()) - throw new InterruptedException(); + checkInterrupted(interruptible); long now = System.currentTimeMillis(); if (now != lastTime) { lastTime = now; @@ -350,13 +349,17 @@ public final class BigSegmentHeader implements SegmentHeader { throws InterruptedException { long end = System.nanoTime() + timeInNanos; registerWait(address); - do { - if (LOCK.tryWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET)) - return true; - detectInterruptionAndDeregisterWait(address, interruptible); - } while (System.nanoTime() <= end); - deregisterWait(address); - return false; + try { + do { + if (LOCK.tryWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET)) + return true; + checkInterrupted(interruptible); + } while (System.nanoTime() <= end); + deregisterWait(address); + return false; + } catch (Throwable t) { + throw tryDeregisterWaitAndRethrow(address, t); + } } /** @@ -366,26 +369,27 @@ public final class BigSegmentHeader implements SegmentHeader { long address, long timeInMillis, boolean interruptible) throws InterruptedException { long lastTime = System.currentTimeMillis(); registerWait(address); - do { - if (LOCK.tryWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET)) - return true; - detectInterruptionAndDeregisterWait(address, interruptible); - long now = System.currentTimeMillis(); - if (now != lastTime) { - lastTime = now; - timeInMillis--; - } - } while (timeInMillis >= 0); - deregisterWait(address); - return false; + try { + do { + if (LOCK.tryWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET)) + return true; + checkInterrupted(interruptible); + long now = System.currentTimeMillis(); + if (now != lastTime) { + lastTime = now; + timeInMillis--; + } + } while (timeInMillis >= 0); + deregisterWait(address); + return false; + } catch (Throwable t) { + throw tryDeregisterWaitAndRethrow(address, t); + } } - private static void detectInterruptionAndDeregisterWait(long address, boolean interruptible) - throws InterruptedException { - if (interruptible && Thread.interrupted()) { - deregisterWait(address); + private static void checkInterrupted(boolean interruptible) throws InterruptedException { + if (interruptible && Thread.interrupted()) throw new InterruptedException(); - } } private static void registerWait(long address) { @@ -446,13 +450,17 @@ public final class BigSegmentHeader implements SegmentHeader { long address, long timeInNanos, boolean interruptible) throws InterruptedException { long end = System.nanoTime() + timeInNanos; registerWait(address); - do { - if (tryUpgradeUpdateToWriteLockAndDeregisterWait0(address)) - return true; - detectInterruptionAndDeregisterWait(address, interruptible); - } while (System.nanoTime() <= end); - deregisterWait(address); - return false; + try { + do { + if (tryUpgradeUpdateToWriteLockAndDeregisterWait0(address)) + return true; + checkInterrupted(interruptible); + } while (System.nanoTime() <= end); + deregisterWait(address); + return false; + } catch (Throwable t) { + throw tryDeregisterWaitAndRethrow(address, t); + } } /** @@ -462,34 +470,35 @@ public final class BigSegmentHeader implements SegmentHeader { long address, long timeInMillis, boolean interruptible) throws InterruptedException { long lastTime = System.currentTimeMillis(); registerWait(address); - do { - if (tryUpgradeUpdateToWriteLockAndDeregisterWait0(address)) - return true; - detectInterruptionAndDeregisterWait(address, interruptible); - long now = System.currentTimeMillis(); - if (now != lastTime) { - lastTime = now; - timeInMillis--; - } - } while (timeInMillis >= 0); - deregisterWait(address); - return false; + try { + do { + if (tryUpgradeUpdateToWriteLockAndDeregisterWait0(address)) + return true; + checkInterrupted(interruptible); + long now = System.currentTimeMillis(); + if (now != lastTime) { + lastTime = now; + timeInMillis--; + } + } while (timeInMillis >= 0); + deregisterWait(address); + return false; + } catch (Throwable t) { + throw tryDeregisterWaitAndRethrow(address, t); + } + } + + private static RuntimeException tryDeregisterWaitAndRethrow(long address, Throwable throwable) { + try { + deregisterWait(address); + } catch (Throwable t) { + throwable.addSuppressed(t); + } + throw Jvm.rethrow(throwable); } private static boolean tryUpgradeUpdateToWriteLockAndDeregisterWait0(long address) { - try { - if (LOCK.tryUpgradeUpdateToWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET)) { - return true; - } - } catch (IllegalMonitorStateException e) { - try { - deregisterWait(address); - } catch (Throwable suppressed) { - e.addSuppressed(suppressed); - } - throw e; - } - return false; + return LOCK.tryUpgradeUpdateToWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET); } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/impl/ChronicleHashCloseOnExitHook.java b/src/main/java/net/openhft/chronicle/hash/impl/ChronicleHashCloseOnExitHook.java index 8b730f493..fc9fdb0fd 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/ChronicleHashCloseOnExitHook.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/ChronicleHashCloseOnExitHook.java @@ -64,13 +64,25 @@ final class ChronicleHashCloseOnExitHook { orderedMaps.descendingMap().values().forEach(h -> { try { h.close(); - } catch (RuntimeException | AssertionError e) { - LOG.error("", e); + } catch (Throwable throwable) { + try { + LOG.error("Error while closing " + h.toIdentityString() + + " during shutdown hook:", throwable); + } catch (Throwable t2) { + // This may occur if the log service has already been shut down. Try to fall + // back to printStackTrace(). + throwable.addSuppressed(t2); + throwable.printStackTrace(); + } } }); - } catch (Throwable t) { - LOG.error("", t); - throw t; + } catch (Throwable throwable) { + try { + LOG.error("Error while closing maps during shutdown hook:", throwable); + } catch (Throwable t2) { + throwable.addSuppressed(t2); + throwable.printStackTrace(); + } } } diff --git a/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java b/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java index 0b9557ef8..9427f32c6 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/CompactOffHeapLinearHashTable.java @@ -62,7 +62,7 @@ public abstract class CompactOffHeapLinearHashTable { public static long capacityFor(long entriesPerSegment) { if (entriesPerSegment < 0L) - throw new AssertionError("entriesPerSegment should be positive"); + throw new AssertionError("entriesPerSegment should be positive: " + entriesPerSegment); long capacity = Maths.nextPower2(entriesPerSegment, 64L); if (entriesPerSegment > MAX_UPPER_BOUND_LOAD_FACTOR * capacity) capacity *= 2; diff --git a/src/main/java/net/openhft/chronicle/hash/impl/InMemoryChronicleHashResourceReleaser.java b/src/main/java/net/openhft/chronicle/hash/impl/InMemoryChronicleHashResourceReleaser.java index ae2a51de4..c0c42954e 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/InMemoryChronicleHashResourceReleaser.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/InMemoryChronicleHashResourceReleaser.java @@ -32,24 +32,23 @@ public final class InMemoryChronicleHashResourceReleaser extends ChronicleHashRe public synchronized void run() { if (memoryResources == null) return; // Already released - // All Throwables are catched because this class is used as Runnable for sun.misc.Cleaner, + // All Throwables are caught because this class is used as Runnable for sun.misc.Cleaner, // hence must not fail - Throwable thrown = null; try { - thrown = doRelease(); + Throwable thrown = doRelease(); + if (thrown != null) { + try { + LOG.error("Error on releasing memory of a Chronicle Map:", thrown); + } catch (Throwable t) { + // This may occur if Releaser is run in a shutdown hook, and the log service has + // already been shut down. Try to fall back to printStackTrace(). + thrown.addSuppressed(t); + thrown.printStackTrace(); + } + } } finally { memoryResources = null; } - if (thrown != null) { - try { - LOG.error("Error on releasing memory of a Chronicle Map:", thrown); - } catch (Throwable t) { - // This may occur if Releaser is run in a shutdown hook, and the log service has - // already been shut down. Try to fall back to printStackTrace(). - thrown.addSuppressed(t); - thrown.printStackTrace(); - } - } } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/impl/PersistedChronicleHashResourceReleaser.java b/src/main/java/net/openhft/chronicle/hash/impl/PersistedChronicleHashResourceReleaser.java index de04c6cdd..f4faaf467 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/PersistedChronicleHashResourceReleaser.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/PersistedChronicleHashResourceReleaser.java @@ -40,25 +40,24 @@ public final class PersistedChronicleHashResourceReleaser public synchronized void run() { if (memoryResources == null) return; // Already released - // All Throwables are catched because this class is used as Runnable for sun.misc.Cleaner, + // All Throwables are caught because this class is used as Runnable for sun.misc.Cleaner, // hence must not fail - Throwable thrown = null; try { - thrown = doRelease(); + Throwable thrown = doRelease(); + if (thrown != null) { + try { + LOG.error("Error on releasing memory or RAF of the Chronicle Map at " + + "file=" + file, thrown); + } catch (Throwable t) { + // This may occur if Releaser is run in a shutdown hook, and the log service has + // already been shut down. Try to fall back to printStackTrace(). + thrown.addSuppressed(t); + thrown.printStackTrace(); + } + } } finally { memoryResources = null; } - if (thrown != null) { - try { - LOG.error("Error on releasing memory and RAF of a Chronicle Map at file=" + file, - thrown); - } catch (Throwable t) { - // This may occur if Releaser is run in a shutdown hook, and the log service has - // already been shut down. Try to fall back to printStackTrace(). - thrown.addSuppressed(t); - thrown.printStackTrace(); - } - } } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java index 85ce9d1be..8b7772ccf 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/VanillaChronicleHash.java @@ -26,6 +26,7 @@ import net.openhft.chronicle.core.OS; import net.openhft.chronicle.hash.*; import net.openhft.chronicle.hash.impl.stage.hash.ChainingInterface; import net.openhft.chronicle.hash.impl.util.BuildVersion; +import net.openhft.chronicle.hash.impl.util.Throwables; import net.openhft.chronicle.hash.impl.util.jna.PosixMsync; import net.openhft.chronicle.hash.impl.util.jna.WindowsMsync; import net.openhft.chronicle.hash.serialization.DataAccess; @@ -421,7 +422,7 @@ public abstract class VanillaChronicleHash contextRef : allContexts) { - ChainingInterface context = contextRef.get(); - if (context != null && context.owner().isAlive()) { - // Ensures that if the thread owning this context will come to access - // chronicleHash concurrently with resource releasing operations below, it will - // fail due to the check in context.lockContextLocally() method. - // If the thread owning this context is currently accessing chronicleHash, - // closeContext() will spin-wait until the end of this access session. - context.closeContext(); + try { + for (WeakReference contextRef : allContexts) { + ChainingInterface context = contextRef.get(); + if (context != null && context.owner().isAlive()) { + // Ensures that if the thread owning this context will come to access + // chronicleHash concurrently with resource releasing operations below, it will + // fail due to the check in context.lockContextLocally() method. + // If the thread owning this context is currently accessing chronicleHash, + // closeContext() will spin-wait until the end of this access session. + context.closeContext(this); + } + } + + resourceReleaser.releaseManually(); + // Releases nothing after resourceReleaser.releaseManually(), only removes the cleaner + // from the internal linked list of all cleaners. + cleaner.clean(); + ChronicleHashCloseOnExitHook.remove(this); + } catch (Throwable t) { + if (thrown == null) { + throw t; + } else { + thrown.addSuppressed(t); + throw Throwables.propagate(thrown); } } - - resourceReleaser.releaseManually(); - // Releases nothing after resourceReleaser.releaseManually(), only removes the cleaner - // from the internal linked list of all cleaners. - cleaner.clean(); - ChronicleHashCloseOnExitHook.remove(this); } @Override @@ -699,8 +711,8 @@ public abstract class VanillaChronicleHash= maxExtraTiers) { - throw new IllegalStateException("Attempt to allocate #" + (tiersInUse + 1) + + throw new IllegalStateException(toIdentityString() + ": " + + "Attempt to allocate #" + (tiersInUse + 1) + " extra segment tier, " + maxExtraTiers + " is maximum.\n" + "Possible reasons include:\n" + " - you have forgotten to configure (or configured wrong) " + @@ -802,19 +815,19 @@ public abstract class VanillaChronicleHash implements HashEntry, ChecksumEntry public void updateChecksum() { checkOnEachPublicOperation.checkOnEachPublicOperation(); if (!hh.h().checksumEntries) { - throw new UnsupportedOperationException( - "Checksum is not stored in this Chronicle Hash"); + throw new UnsupportedOperationException(hh.h().toIdentityString() + + ": Checksum is not stored in this Chronicle Hash"); } s.innerUpdateLock.lock(); initDelayedUpdateChecksum(true); @@ -172,8 +172,8 @@ public abstract class HashEntryStages implements HashEntry, ChecksumEntry public boolean checkSum() { checkOnEachPublicOperation.checkOnEachPublicOperation(); if (!hh.h().checksumEntries) { - throw new UnsupportedOperationException( - "Checksum is not stored in this Chronicle Hash"); + throw new UnsupportedOperationException(hh.h().toIdentityString() + + ": Checksum is not stored in this Chronicle Hash"); } // This is needed, because a concurrent update lock holder might perform an entry update, // but not yet written a checksum (because checksum write is delayed to the update unlock). diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/HashLookupSearch.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/HashLookupSearch.java index c4461b3b7..28d8ccfd7 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/HashLookupSearch.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/HashLookupSearch.java @@ -71,7 +71,8 @@ public abstract class HashLookupSearch { return hl.value(entry); } } - throw new IllegalStateException("HashLookup overflow should never occur"); + throw new IllegalStateException(hh.h().toIdentityString() + + ": HashLookup overflow should never occur"); } public void found() { diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/ReadLock.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/ReadLock.java index 0458891a1..9e5e6d6d8 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/ReadLock.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/ReadLock.java @@ -18,6 +18,7 @@ package net.openhft.chronicle.hash.impl.stage.entry; import net.openhft.chronicle.hash.impl.stage.hash.CheckOnEachPublicOperation; +import net.openhft.chronicle.hash.locks.InterProcessDeadLockException; import net.openhft.chronicle.hash.locks.InterProcessLock; import net.openhft.sg.StageRef; import net.openhft.sg.Staged; @@ -46,8 +47,13 @@ public class ReadLock implements InterProcessLock { public void lock() { checkOnEachPublicOperation.checkOnEachLockOperation(); if (s.localLockState == UNLOCKED) { - if (s.readZero() && s.updateZero() && s.writeZero()) - s.segmentHeader.readLock(s.segmentHeaderAddress); + if (s.readZero() && s.updateZero() && s.writeZero()) { + try { + s.segmentHeader.readLock(s.segmentHeaderAddress); + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); + } + } s.incrementRead(); s.setLocalLockState(READ_LOCKED); } @@ -59,8 +65,13 @@ public class ReadLock implements InterProcessLock { if (Thread.interrupted()) throw new InterruptedException(); if (s.localLockState == UNLOCKED) { - if (s.readZero() && s.updateZero() && s.writeZero()) - s.segmentHeader.readLockInterruptibly(s.segmentHeaderAddress); + if (s.readZero() && s.updateZero() && s.writeZero()) { + try { + s.segmentHeader.readLockInterruptibly(s.segmentHeaderAddress); + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); + } + } s.incrementRead(); s.setLocalLockState(READ_LOCKED); } diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java index f8d2d9a27..c364db7f3 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/SegmentStages.java @@ -30,6 +30,7 @@ import net.openhft.chronicle.hash.impl.stage.hash.Chaining; import net.openhft.chronicle.hash.impl.stage.hash.CheckOnEachPublicOperation; import net.openhft.chronicle.hash.impl.stage.hash.LogHolder; import net.openhft.chronicle.hash.impl.stage.query.KeySearch; +import net.openhft.chronicle.hash.locks.InterProcessDeadLockException; import net.openhft.chronicle.hash.locks.InterProcessLock; import net.openhft.chronicle.map.impl.IterationContext; import net.openhft.sg.Stage; @@ -260,9 +261,9 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { public abstract boolean locksInit(); void initLocks() { - // This is a dummy check needed to make the Locks stage depend on the chaining.Used stage, - // to move the chaining.Used stage up in topological order, to make it closed later in - // the global context close() method, to ensure the context is unlocked (this is done in + // This is a dummy check needed to make the Locks stage dependent on the chaining.Used + // stage, to move the chaining.Used stage up in topological order, to make it closed later + // in the global context close() method, to ensure the context is unlocked (this is done in // the chaining.closeUsed() method) after all other stages, potentially accessing Map's // off-heap memory, are closed. assert chaining.used; @@ -273,7 +274,8 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { // SegmentHeader is init, Locks stage is closed (and then need to be re-init again), as // a dependency of SegmentHeader. So ensuring SegmentHeader is always init before Locks // allows to avoid redundant work. - if (segmentHeader == null) throw new AssertionError(); + if (segmentHeader == null) + throw new AssertionError(); localLockState = UNLOCKED; int indexOfThisContext = chaining.indexInContextChain; for (int i = indexOfThisContext - 1; i >= 0; i--) { @@ -386,8 +388,8 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { if (innermostContextOnThisSegment.getClass() == this.getClass()) { Data key = ((KeySearch) innermostContextOnThisSegment).inputKey; if (Objects.equals(key, ((KeySearch) (Object) this).inputKey)) { - throw new IllegalStateException("Nested same-thread contexts cannot access " + - "the same key " + key); + throw new IllegalStateException(hh.h().toIdentityString() + + ": Nested same-thread contexts cannot access the same key " + key); } } } @@ -410,8 +412,10 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { @Stage("Locks") private void verifyInnermostContext() { - if (nextNode != null) - throw new IllegalStateException("Attempt to close contexts not structurally"); + if (nextNode != null) { + throw new IllegalStateException( + hh.h().toIdentityString() + ": Attempt to close contexts not structurally"); + } } @Stage("Locks") @@ -447,8 +451,8 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { public void checkIterationContextNotLockedInThisThread() { if (chaining.rootContextInThisThread.iterationContextLockedInThisThread) { - throw new IllegalStateException("Update or Write locking is forbidden in the context" + - "of locked iteration context"); + throw new IllegalStateException(hh.h().toIdentityString() + ": Update or Write " + + "locking is forbidden in the context of locked iteration context"); } } @@ -465,8 +469,8 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { } @Stage("Locks") - public String debugContextsAndLocks() { - String message = ""; + public RuntimeException debugContextsAndLocks(InterProcessDeadLockException e) { + String message = hh.h().toIdentityString() + ":\n"; message += "Contexts locked on this segment:\n"; for (LocksInterface cxt = rootContextLockedOnThisSegment; cxt != null; @@ -478,7 +482,7 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { LocksInterface cxt = chaining.contextAtIndexInChain(i); message += cxt.debugLocksState() + "\n"; } - return message; + throw new InterProcessDeadLockException(message, e); } @Override @@ -595,8 +599,10 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { } public void prevTier() { - if (tier == 0) - throw new IllegalStateException("first tier doesn't have previous"); + if (tier == 0) { + throw new IllegalStateException( + hh.h().toIdentityString() + ": first tier doesn't have previous"); + } initSegmentTier(tier - 1, prevTierIndex()); } @@ -657,7 +663,8 @@ public abstract class SegmentStages implements SegmentLock, LocksInterface { public long allocReturnCode(int chunks) { VanillaChronicleHash h = hh.h(); if (chunks > h.maxChunksPerEntry) { - throw new IllegalArgumentException("Entry is too large: requires " + chunks + + throw new IllegalArgumentException(hh.h().toIdentityString() + + ": Entry is too large: requires " + chunks + " chunks, " + h.maxChunksPerEntry + " is maximum."); } long lowestPossiblyFreeChunk = lowestPossiblyFreeChunk(); diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/UpdateLock.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/UpdateLock.java index f01e1dac6..0083f565a 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/UpdateLock.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/UpdateLock.java @@ -17,8 +17,9 @@ package net.openhft.chronicle.hash.impl.stage.entry; +import net.openhft.chronicle.hash.impl.VanillaChronicleHashHolder; import net.openhft.chronicle.hash.impl.stage.hash.CheckOnEachPublicOperation; -import net.openhft.chronicle.hash.impl.stage.hash.LogHolder; +import net.openhft.chronicle.hash.locks.InterProcessDeadLockException; import net.openhft.chronicle.hash.locks.InterProcessLock; import net.openhft.sg.StageRef; import net.openhft.sg.Staged; @@ -32,8 +33,8 @@ import static net.openhft.chronicle.hash.impl.LocalLockState.UPDATE_LOCKED; @Staged public class UpdateLock implements InterProcessLock { + @StageRef VanillaChronicleHashHolder hh; @StageRef CheckOnEachPublicOperation checkOnEachPublicOperation; - @StageRef LogHolder logHolder; @StageRef SegmentStages s; @StageRef HashEntryStages entry; @@ -54,9 +55,8 @@ public class UpdateLock implements InterProcessLock { throw forbiddenUpdateLockWhenOuterContextReadLocked(); try { s.segmentHeader.updateLock(s.segmentHeaderAddress); - } catch (RuntimeException e) { - logHolder.LOG.error(s.debugContextsAndLocks()); - throw e; + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); } } s.incrementUpdate(); @@ -75,7 +75,8 @@ public class UpdateLock implements InterProcessLock { */ @NotNull private IllegalMonitorStateException forbiddenUpgrade() { - return new IllegalMonitorStateException("Cannot upgrade from read to update lock"); + return new IllegalMonitorStateException( + hh.h().toIdentityString() + ": Cannot upgrade from read to update lock"); } /** @@ -83,9 +84,9 @@ public class UpdateLock implements InterProcessLock { */ @NotNull private IllegalStateException forbiddenUpdateLockWhenOuterContextReadLocked() { - return new IllegalStateException("Cannot acquire update lock, because outer context " + - "holds read lock. In this case you should acquire update lock in the outer " + - "context up front"); + return new IllegalStateException(hh.h().toIdentityString() + + ": Cannot acquire update lock, because outer context holds read lock. " + + "In this case you should acquire update lock in the outer context up front"); } @Override @@ -99,7 +100,11 @@ public class UpdateLock implements InterProcessLock { if (s.updateZero() && s.writeZero()) { if (!s.readZero()) throw forbiddenUpdateLockWhenOuterContextReadLocked(); - s.segmentHeader.updateLockInterruptibly(s.segmentHeaderAddress); + try { + s.segmentHeader.updateLockInterruptibly(s.segmentHeaderAddress); + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); + } } s.incrementUpdate(); s.setLocalLockState(UPDATE_LOCKED); @@ -138,8 +143,10 @@ public class UpdateLock implements InterProcessLock { case UPDATE_LOCKED: case WRITE_LOCKED: return true; + default: + throw new IllegalStateException(hh.h().toIdentityString() + + ": unexpected localLockState=" + s.localLockState); } - throw new AssertionError(); } @Override @@ -170,8 +177,10 @@ public class UpdateLock implements InterProcessLock { case UPDATE_LOCKED: case WRITE_LOCKED: return true; + default: + throw new IllegalStateException(hh.h().toIdentityString() + + ": unexpected localLockState=" + s.localLockState); } - throw new AssertionError(); } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/WriteLock.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/WriteLock.java index ec869378c..7f43ba21a 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/WriteLock.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/entry/WriteLock.java @@ -17,7 +17,9 @@ package net.openhft.chronicle.hash.impl.stage.entry; +import net.openhft.chronicle.hash.impl.VanillaChronicleHashHolder; import net.openhft.chronicle.hash.impl.stage.hash.CheckOnEachPublicOperation; +import net.openhft.chronicle.hash.locks.InterProcessDeadLockException; import net.openhft.chronicle.hash.locks.InterProcessLock; import net.openhft.sg.StageRef; import net.openhft.sg.Staged; @@ -31,6 +33,7 @@ import static net.openhft.chronicle.hash.impl.LocalLockState.WRITE_LOCKED; @Staged public class WriteLock implements InterProcessLock { + @StageRef VanillaChronicleHashHolder hh; @StageRef CheckOnEachPublicOperation checkOnEachPublicOperation; @StageRef SegmentStages s; @StageRef HashEntryStages entry; @@ -53,7 +56,11 @@ public class WriteLock implements InterProcessLock { } else { if (!s.readZero()) throw forbiddenWriteLockWhenOuterContextReadLocked(); - s.segmentHeader.writeLock(s.segmentHeaderAddress); + try { + s.segmentHeader.writeLock(s.segmentHeaderAddress); + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); + } } } s.incrementWrite(); @@ -64,7 +71,11 @@ public class WriteLock implements InterProcessLock { case UPDATE_LOCKED: if (s.writeZero()) { assert !s.updateZero(); - s.segmentHeader.upgradeUpdateToWriteLock(s.segmentHeaderAddress); + try { + s.segmentHeader.upgradeUpdateToWriteLock(s.segmentHeaderAddress); + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); + } } s.decrementUpdate(); s.incrementWrite(); @@ -79,7 +90,8 @@ public class WriteLock implements InterProcessLock { */ @NotNull private IllegalMonitorStateException forbiddenUpgrade() { - return new IllegalMonitorStateException("Cannot upgrade from read to write lock"); + return new IllegalMonitorStateException( + hh.h().toIdentityString() + ": Cannot upgrade from read to write lock"); } /** @@ -87,9 +99,9 @@ public class WriteLock implements InterProcessLock { */ @NotNull private IllegalStateException forbiddenWriteLockWhenOuterContextReadLocked() { - return new IllegalStateException("Cannot acquire write lock, because outer context " + - "holds read lock. In this case you should acquire update lock in the outer " + - "context up front"); + return new IllegalStateException(hh.h().toIdentityString() + + ": Cannot acquire write lock, because outer context holds read lock. " + + "In this case you should acquire update lock in the outer context up front"); } @Override @@ -107,7 +119,11 @@ public class WriteLock implements InterProcessLock { } else { if (!s.readZero()) throw forbiddenWriteLockWhenOuterContextReadLocked(); - s.segmentHeader.writeLockInterruptibly(s.segmentHeaderAddress); + try { + s.segmentHeader.writeLockInterruptibly(s.segmentHeaderAddress); + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); + } } } s.incrementWrite(); @@ -118,7 +134,12 @@ public class WriteLock implements InterProcessLock { case UPDATE_LOCKED: if (s.writeZero()) { assert !s.updateZero(); - s.segmentHeader.upgradeUpdateToWriteLockInterruptibly(s.segmentHeaderAddress); + try { + s.segmentHeader.upgradeUpdateToWriteLockInterruptibly( + s.segmentHeaderAddress); + } catch (InterProcessDeadLockException e) { + throw s.debugContextsAndLocks(e); + } } s.decrementUpdate(); s.incrementWrite(); @@ -180,8 +201,10 @@ public class WriteLock implements InterProcessLock { } case WRITE_LOCKED: return true; + default: + throw new IllegalStateException(hh.h().toIdentityString() + + ": unexpected localLockState=" + s.localLockState); } - throw new AssertionError(); } @Override @@ -240,8 +263,10 @@ public class WriteLock implements InterProcessLock { } case WRITE_LOCKED: return true; + default: + throw new IllegalStateException(hh.h().toIdentityString() + + ": unexpected localLockState=" + s.localLockState); } - throw new AssertionError(); } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/Chaining.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/Chaining.java index 5234a5c9e..7780de150 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/Chaining.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/Chaining.java @@ -66,7 +66,7 @@ public abstract class Chaining extends ChainingInterface { * context in the beginning of each usage of the context. Previously, this field was final and * set only once during context creation. It was preventing ChronicleMap objects from becoming * dead and collected by the GC, while any thread, from which the ChronicleMap was accessed - * (i. e, a thread local context created), is alive. + * (hence a thread local context created), is alive. * *

The chain of strong references: * 1) Thread -> @@ -123,11 +123,11 @@ public abstract class Chaining extends ChainingInterface { } int maxNestedContexts = 1 << 10; if (contextChain.size() > maxNestedContexts) { - throw new IllegalStateException("More than " + maxNestedContexts + - " nested ChronicleHash contexts are not supported. Very probable that " + - "you simply forgot to close context somewhere (recommended to use " + - "try-with-resources statement). " + - "Otherwise this is a bug, please report with this " + + throw new IllegalStateException(map.toIdentityString() + + ": More than " + maxNestedContexts + " nested ChronicleHash contexts\n" + + "are not supported. Very probable that you simply forgot to close context\n" + + "somewhere (recommended to use try-with-resources statement).\n" + + "Otherwise this is a bug, please report with this\n" + "stack trace on https://github.com/OpenHFT/Chronicle-Map/issues"); } //noinspection unchecked diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/OwnerThreadHolder.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/OwnerThreadHolder.java index 6d6784f5f..eb3f90673 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/OwnerThreadHolder.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/OwnerThreadHolder.java @@ -17,6 +17,8 @@ package net.openhft.chronicle.hash.impl.stage.hash; +import net.openhft.chronicle.hash.impl.VanillaChronicleHashHolder; +import net.openhft.sg.StageRef; import net.openhft.sg.Staged; import java.util.ConcurrentModificationException; @@ -24,12 +26,14 @@ import java.util.ConcurrentModificationException; @Staged public class OwnerThreadHolder { + @StageRef VanillaChronicleHashHolder hh; + final Thread owner = Thread.currentThread(); public void checkAccessingFromOwnerThread() { if (owner != Thread.currentThread()) { - throw new ConcurrentModificationException( - "Context shouldn't be accessed from multiple threads"); + throw new ConcurrentModificationException(hh.h().toIdentityString() + + ": Context shouldn't be accessed from multiple threads"); } } } diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/ThreadLocalState.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/ThreadLocalState.java index cb37cfa2f..0c674b7d6 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/ThreadLocalState.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/hash/ThreadLocalState.java @@ -42,7 +42,7 @@ public abstract class ThreadLocalState { ThreadLocalState.class.getDeclaredField("contextLock"); contextLockField.setAccessible(true); CONTEXT_LOCK_OFFSET = MEMORY.getFieldOffset(contextLockField); - } catch (NoSuchFieldException | SecurityException e) { + } catch (NoSuchFieldException e) { throw new AssertionError(e); } } @@ -68,7 +68,7 @@ public abstract class ThreadLocalState { // Don't extract this hash().isOpen() and the one above, because they could different // results (the first (above) could return true, the second (below) - false). if (!hash.isOpen()) - throw new ChronicleHashClosedException(); + throw new ChronicleHashClosedException(hash); throw new AssertionError("Unknown context lock state: " + contextLock); } } @@ -81,15 +81,17 @@ public abstract class ThreadLocalState { MEMORY.writeOrderedInt(this, CONTEXT_LOCK_OFFSET, CONTEXT_UNLOCKED); } - public void closeContext() { + public void closeContext(ChronicleHash hash) { if (tryCloseContext()) return; // If first attempt of closing a context (i. e. moving from unused to closed state) failed, // it means that the context is still in use. If this context belongs to the current thread, // this is a bug, because we cannot "wait" until context is unused in the same thread: - if (owner() == Thread.currentThread()) - throw new IllegalStateException("Attempt to close a Chronicle Hash in the context " + - "of not yet finished query or iteration"); + if (owner() == Thread.currentThread()) { + throw new IllegalStateException(hash.toIdentityString() + + ": Attempt to close a Chronicle Hash in the context " + + "of not yet finished query or iteration"); + } // If the context belongs to a different thread, wait until that thread finishes it's work // with the context: @@ -107,9 +109,9 @@ public abstract class ThreadLocalState { timeoutMillis--; } } while (timeoutMillis >= 0); - throw new RuntimeException("Failed to close a context, belonging to the thread\n" + - owner() + ", in the state: " + owner().getState() + ", current stack trace:\n" + - Arrays.toString(owner().getStackTrace()) + "\n" + + throw new RuntimeException(hash.toIdentityString() + + ": Failed to close a context, belonging to the thread\n" + + owner() + ", in the state: " + owner().getState() + "\n" + "Possible reasons:\n" + "- The context owner thread exited before closing this context. Ensure that you\n" + "always close opened Chronicle Map's contexts, the best way to do this is to use\n" + diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/HashSegmentIteration.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/HashSegmentIteration.java index d05b43e46..646e2a40d 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/HashSegmentIteration.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/HashSegmentIteration.java @@ -162,7 +162,8 @@ public abstract class HashSegmentIteration> // entry } while (currentHashLookupPos != startPos || steps == 0); if (!interrupted && leftEntries > 0) { - throw new IllegalStateException("We a tier without interruption, " + + throw new IllegalStateException(hh.h().toIdentityString() + + ": We a tier without interruption, " + "but according to tier counters there should be " + leftEntries + " more entries. Size diverged?"); } @@ -178,8 +179,10 @@ public abstract class HashSegmentIteration> } public void checkEntryNotRemovedOnThisIteration() { - if (entryRemovedOnThisIterationInit()) - throw new IllegalStateException("Entry was already removed on this iteration"); + if (entryRemovedOnThisIterationInit()) { + throw new IllegalStateException( + hh.h().toIdentityString() + ": Entry was already removed on this iteration"); + } } @Override diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/TierRecovery.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/TierRecovery.java index 4347b0acc..be13a9b1e 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/TierRecovery.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/iter/TierRecovery.java @@ -241,7 +241,7 @@ public class TierRecovery { try { e.readExistingEntry(entryPos); } catch (Exception e) { - log.error("Exception while reading entry key size: {}", e); + log.error("Exception while reading entry key size:", e); return -1; } if (e.keyEnd() > s.segmentBytes.capacity()) { @@ -275,7 +275,7 @@ public class TierRecovery { return -1; } } catch (Exception ex) { - log.error("Exception while reading entry value size: {}, key: {}", ex, e.key()); + log.error("Exception while reading entry value size, key: " + e.key(), ex); return -1; } diff --git a/src/main/java/net/openhft/chronicle/hash/impl/stage/query/HashQuery.java b/src/main/java/net/openhft/chronicle/hash/impl/stage/query/HashQuery.java index 71bfca305..86d081760 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/stage/query/HashQuery.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/stage/query/HashQuery.java @@ -137,7 +137,8 @@ public abstract class HashQuery implements SetEntry { ks.setSearchState(ABSENT); initPresenceOfEntry(EntryPresence.ABSENT); } else { - throw new IllegalStateException("Entry is absent when doRemove() is called"); + throw new IllegalStateException( + hh.h().toIdentityString() + ": Entry is absent when doRemove() is called"); } } } diff --git a/src/main/java/net/openhft/chronicle/hash/locks/InterProcessDeadLockException.java b/src/main/java/net/openhft/chronicle/hash/locks/InterProcessDeadLockException.java new file mode 100644 index 000000000..65f70e072 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/hash/locks/InterProcessDeadLockException.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Roman Leventov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package net.openhft.chronicle.hash.locks; + +/** + * Thrown from {@link InterProcessLock#lock()} and {@link InterProcessLock#lockInterruptibly()} when + * if fails to acquire the lock for some implementation-defined period of time. + */ +public final class InterProcessDeadLockException extends RuntimeException { + private static final long serialVersionUID = 0L; + + /** + * Constructs a new {@code InterProcessDeadLockException} with the specified detail message. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * {@link #getMessage()} method. + */ + public InterProcessDeadLockException(String message) { + super(message); + } + + /** + * Constructs a new {@code InterProcessDeadLockException} with the specified detail message and + * cause. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * {@link #getMessage()} method. + * @param cause the cause (which is saved for later retrieval by the {@link #getCause()} method) + */ + public InterProcessDeadLockException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/net/openhft/chronicle/hash/locks/InterProcessLock.java b/src/main/java/net/openhft/chronicle/hash/locks/InterProcessLock.java index 099e4046e..7d121447a 100644 --- a/src/main/java/net/openhft/chronicle/hash/locks/InterProcessLock.java +++ b/src/main/java/net/openhft/chronicle/hash/locks/InterProcessLock.java @@ -58,7 +58,7 @@ public interface InterProcessLock extends Lock { boolean isHeldByCurrentThread(); /** - * Acquires the lock, if this lock (or a stronger-level lock, in the context of {@link + * Acquires the lock. If this lock (or a stronger-level lock, in the context of {@link * InterProcessReadWriteUpdateLock}) is already held by the current thread, this call returns * immediately. * @@ -66,15 +66,43 @@ public interface InterProcessLock extends Lock { * threshold time spent in a busy loop, the thread might be disabled for thread * scheduling purposes and lay dormant until the lock has been acquired. After some * implementation-defined time spent in waiting for the lock acquisition, - * {@link RuntimeException} is thrown. + * {@link InterProcessDeadLockException} is thrown. * * @throws IllegalMonitorStateException if this method call observes illegal lock state, or some * lock limitations reached (e. g. maximum read lock holders) - * @throws RuntimeException if fails to acquire a lock for some finite time + * @throws InterProcessDeadLockException if fails to acquire a lock for some finite time */ @Override void lock(); + /** + * Acquires the lock unless the current thread is {@linkplain Thread#interrupt interrupted}. If + * the current thread is not interrupted, and this lock (or a stronger-level lock, in the + * context of {@link InterProcessReadWriteUpdateLock}) is already held by the current thread, + * this call returns immediately. + * + *

If the lock is not available then the current thread enters a busy loop, and after some + * threshold time spend in a busy loop, the thread might be disabled for thread + * scheduling purposes and lay dormant until one of three things happens: + * + *

    + *
  • The lock is acquired by the current thread, then {@code lockInterruptibly()} successfully + * returns. + *
  • Some other thread {@linkplain Thread#interrupt interrupts} the current thread, then + * {@link InterruptedException} is thrown and the current thread's interrupted status is + * cleared. + *
  • Some implementation-defined time is spent in waiting for the lock acquisition, then + * {@link InterProcessDeadLockException} is thrown. + *
+ * + * @throws InterruptedException if the current thread is interrupted while acquiring the lock + * @throws IllegalMonitorStateException if this method call observes illegal lock state, or some + * lock limitations reached (e. g. maximum read lock holders) + * @throws InterProcessDeadLockException if fails to acquire a lock for some finite time + */ + @Override + void lockInterruptibly() throws InterruptedException; + /** * Acquires the lock only if it is free at the time of invocation, also if the lock is already * held by the current thread, this call immediately returns {@code true}. diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java index 1dd24b845..9e0123196 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/InstanceCreatingMarshaller.java @@ -67,7 +67,7 @@ public abstract class InstanceCreatingMarshaller implements Marshallable { return ObjectUtils.newInstance(tClass); } catch (Exception e) { throw new IllegalStateException("Some of default marshallers, chosen for the type\n" + - tClass + " by default, delegates to \n" + + tClass + " by default, delegate to \n" + this.getClass().getName() + " which assumes the type has a public no-arg\n" + "constructor. If this is not true, you should either extend the marshaller,\n" + "overriding createInstance() and copy() (if defined), and the extending\n" + diff --git a/src/main/java/net/openhft/chronicle/hash/serialization/impl/SerializationBuilder.java b/src/main/java/net/openhft/chronicle/hash/serialization/impl/SerializationBuilder.java index 155c750de..46b7e1d3b 100644 --- a/src/main/java/net/openhft/chronicle/hash/serialization/impl/SerializationBuilder.java +++ b/src/main/java/net/openhft/chronicle/hash/serialization/impl/SerializationBuilder.java @@ -94,7 +94,8 @@ public final class SerializationBuilder implements Cloneable { reader(new ByteableSizedReader<>((Class) tClass)); dataAccess((DataAccess) new ByteableDataAccess<>((Class) tClass)); try { - sizeMarshaller(constant(((Byteable) OS.memory().allocateInstance(tClass)).maxSize())); + long byteableSize = ((Byteable) OS.memory().allocateInstance(tClass)).maxSize(); + sizeMarshaller(constant(byteableSize)); } catch (InstantiationException e) { throw new IllegalStateException(e); } diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java index e6104e052..8d50a19b1 100755 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapBuilder.java @@ -573,8 +573,6 @@ public final class ChronicleMapBuilder implements * If value size is always the same, call {@link #constantValueSizeBySample(Object)} method * instead of this one. * - *

Example: If you - * *

{@code ChronicleHashBuilder} implementation heuristically chooses {@linkplain * #actualChunkSize(int) the actual chunk size} based on this configuration and the key size, * that, however, might result to quite high internal fragmentation, i. e. losses because only @@ -1547,13 +1545,13 @@ public final class ChronicleMapBuilder implements } prepareMapPublication(result); return result; - } catch (Throwable t) { + } catch (Throwable throwable) { try { resourceReleaser.releaseManually(); } catch (Exception e) { - t.addSuppressed(e); + throwable.addSuppressed(e); } - throw Throwables.propagateNotWrapping(t, IOException.class); + throw Throwables.propagateNotWrapping(throwable, IOException.class); } } @@ -1568,7 +1566,8 @@ public final class ChronicleMapBuilder implements /** * @return size of the self bootstrapping header */ - private int waitUntilReady(RandomAccessFile raf, boolean recover) throws IOException { + private int waitUntilReady(RandomAccessFile raf, File file, boolean recover) + throws IOException { FileChannel fileChannel = raf.getChannel(); ByteBuffer sizeWordBuffer = ByteBuffer.allocate(4); @@ -1594,8 +1593,13 @@ public final class ChronicleMapBuilder implements try { Thread.sleep(100); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); if (recover) { - break; + if (lastReadHeaderSize == -1) { + throw new ChronicleHashRecoveryFailedException(e); + } else { + return lastReadHeaderSize; + } } else { throw new IOException(e); } @@ -1603,13 +1607,15 @@ public final class ChronicleMapBuilder implements } if (recover) { if (lastReadHeaderSize == -1) { - throw new ChronicleHashRecoveryFailedException("File header is not recoverable"); + throw new ChronicleHashRecoveryFailedException( + "File header is not recoverable, file=" + file); } else { return lastReadHeaderSize; } } else { - throw new IOException("Unable to wait until the file is ready, likely the process " + - "which created the file crashed or hung for more than 1 minute"); + throw new IOException("Unable to wait until the file=" + file + + " is ready, likely the process which created the file crashed or hung " + + "for more than 1 minute"); } } @@ -1617,10 +1623,11 @@ public final class ChronicleMapBuilder implements * @return ByteBuffer, in [position, limit) range the self bootstrapping header is read */ private ByteBuffer checkSumSelfBootstrappingHeader( - RandomAccessFile raf, int headerSize, boolean recover) throws IOException { + File file, RandomAccessFile raf, int headerSize, boolean recover) throws IOException { if (raf.length() < headerSize + SELF_BOOTSTRAPPING_HEADER_OFFSET) { - throw throwRecoveryOrReturnIOException("The file is shorter than the header size: " + - headerSize + ", file size: " + raf.length(), recover); + throw throwRecoveryOrReturnIOException(file, + "The file is shorter than the header size: " + headerSize + + ", file size: " + raf.length(), recover); } FileChannel fileChannel = raf.getChannel(); ByteBuffer headerBuffer = ByteBuffer.allocate( @@ -1628,26 +1635,26 @@ public final class ChronicleMapBuilder implements headerBuffer.order(LITTLE_ENDIAN); readFully(fileChannel, 0, headerBuffer); if (headerBuffer.remaining() > 0) { - throw throwRecoveryOrReturnIOException("Unable to read the header fully, " + + throw throwRecoveryOrReturnIOException(file, "Unable to read the header fully, " + headerBuffer.remaining() + " is remaining to read, likely the file was " + "truncated", recover); } int sizeWord = headerBuffer.getInt(SIZE_WORD_OFFSET); if (!SizePrefixedBlob.isReady(sizeWord)) { if (recover) { - LOG.error("size-prefixed blob readiness bit is set to NOT_COMPLETE"); + LOG.error("file={}: size-prefixed blob readiness bit is set to NOT_COMPLETE", file); // the bit will be overwritten to READY in the end of recovery procedure, so nothing // to fix right here } else { - throw new IOException("sizeWord is not ready: " + sizeWord); + throw new IOException("file=" + file+ ": sizeWord is not ready: " + sizeWord); } } long checkSum = headerChecksum(headerBuffer, headerSize); long storedChecksum = headerBuffer.getLong(HEADER_OFFSET); if (storedChecksum != checkSum) { - throw throwRecoveryOrReturnIOException("Self Bootstrapping Header checksum doesn't " + - "match the stored checksum: " + storedChecksum + ", computed: " + checkSum, - recover); + throw throwRecoveryOrReturnIOException(file, + "Self Bootstrapping Header checksum doesn't match the stored checksum: " + + storedChecksum + ", computed: " + checkSum, recover); } headerBuffer.position(SELF_BOOTSTRAPPING_HEADER_OFFSET); return headerBuffer; @@ -1668,7 +1675,7 @@ public final class ChronicleMapBuilder implements boolean recover, boolean overrideBuilderConfig) throws IOException { try { - int headerSize = waitUntilReady(raf, recover); + int headerSize = waitUntilReady(raf, file, recover); FileChannel fileChannel = raf.getChannel(); ByteBuffer headerBuffer; if (overrideBuilderConfig) { @@ -1676,15 +1683,15 @@ public final class ChronicleMapBuilder implements headerBuffer = writeHeader(fileChannel, mapObjectForHeaderOverwrite); headerSize = headerBuffer.remaining(); } else { - headerBuffer = checkSumSelfBootstrappingHeader(raf, headerSize, recover); - assert headerSize == headerBuffer.remaining(); + headerBuffer = checkSumSelfBootstrappingHeader(file, raf, headerSize, recover); + if (headerSize != headerBuffer.remaining()) + throw new AssertionError(); } Bytes headerBytes = Bytes.wrapForRead(headerBuffer); headerBytes.readPosition(headerBuffer.position()); headerBytes.readLimit(headerBuffer.limit()); Wire wire = new TextWire(headerBytes); VanillaChronicleMap map = wire.getValueIn().typedMarshallable(); - assert map != null; map.initBeforeMapping(file, raf, headerBuffer.limit(), recover); long dataStoreSize = map.globalMutableState().getDataStoreSize(); if (!recover && dataStoreSize > file.length()) { @@ -1708,7 +1715,7 @@ public final class ChronicleMapBuilder implements if (recover && !(e instanceof IOException) && !(e instanceof ChronicleHashRecoveryFailedException)) throw new ChronicleHashRecoveryFailedException(e); - throw e; + throw Throwables.propagateNotWrapping(e, IOException.class); } } @@ -1723,13 +1730,13 @@ public final class ChronicleMapBuilder implements map.createInMemoryStoreAndSegments(resourceReleaser); prepareMapPublication(map); return map; - } catch (Throwable t) { + } catch (Throwable throwable) { try { resourceReleaser.releaseManually(); } catch (Exception e) { - t.addSuppressed(e); + throwable.addSuppressed(e); } - throw Throwables.propagate(t); + throw Throwables.propagate(throwable); } } @@ -1799,7 +1806,9 @@ public final class ChronicleMapBuilder implements try { cleanupThread.join(); } catch (InterruptedException e) { - LOG.error("", e); + LOG.warn(map.toIdentityString() + + ": Interrupted while waiting for the cleanup thread to cease"); + Thread.currentThread().interrupt(); } }); } diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMapIterator.java b/src/main/java/net/openhft/chronicle/map/ChronicleMapIterator.java index 777e73c33..97a081c4f 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMapIterator.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMapIterator.java @@ -42,8 +42,8 @@ abstract class ChronicleMapIterator implements Iterator, Consumer implements Iterator, Consumer implements Iterator, Consumer) and map.putAll() methods require the JSON XStream serializer, " + + static final String logErrorSuggestXStreem = + "map.getAll() and map.putAll() methods require the JSON XStream serializer, " + "we don't include these artifacts by default as some users don't require this functionality. " + "Please add the following artifacts to your project\n" + "\n" + @@ -100,8 +99,7 @@ class JsonSerializer { " org.codehaus.jettison\n" + " jettison\n" + " 1.3.6\n" + - "\n", e); - } + "\n"; private static void registerChronicleMapConverter(Map map, XStream xstream) { xstream.registerConverter(new VanillaChronicleMapConverter<>(map)); diff --git a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java index b4763b108..978e6adcd 100644 --- a/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ReplicatedChronicleMap.java @@ -264,15 +264,19 @@ public class ReplicatedChronicleMap extends VanillaChronicleMap public final V checkValue(Object value) { if (!valueClass.isInstance(value)) { - throw new ClassCastException("Value must be a " + valueClass.getName() + - " but was a " + value.getClass()); + throw new ClassCastException(toIdentityString() + ": Value must be a " + + valueClass.getName() + " but was a " + value.getClass()); } return (V) value; } @@ -206,20 +206,21 @@ public class VanillaChronicleMap q.writeLock().lock(); checkAcquiredUsing(acquireUsingBody(q, usingValue), usingValue); return q.acquireHandle(); - } catch (Throwable e) { + } catch (Throwable throwable) { try { q.close(); - } catch (Throwable suppressed) { - e.addSuppressed(suppressed); + } catch (Throwable t) { + throwable.addSuppressed(t); } - throw e; + throw throwable; } } - private static void checkAcquiredUsing(V acquired, V using) { + private void checkAcquiredUsing(V acquired, V using) { if (acquired != using) { - throw new IllegalArgumentException("acquire*() MUST reuse the given " + - "value. Given value " + using + " cannot be reused to read " + acquired); + throw new IllegalArgumentException(toIdentityString() + + ": acquire*() MUST reuse the given value. Given value " + using + + " cannot be reused to read " + acquired); } } @@ -487,8 +488,10 @@ public class VanillaChronicleMap break searchLoop; } hlPos = hl.step(hlPos); - if (hlPos == searchStartPos) - throw new IllegalStateException("HashLookup overflow should never occur"); + if (hlPos == searchStartPos) { + throw new IllegalStateException( + toIdentityString() + ": HashLookup overflow should never occur"); + } if ((hl.key(entry)) == searchKey) { entryPos = hl.value(entry); diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/data/DummyValueZeroData.java b/src/main/java/net/openhft/chronicle/map/impl/stage/data/DummyValueZeroData.java index 96a505e98..8e5673044 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/data/DummyValueZeroData.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/data/DummyValueZeroData.java @@ -58,11 +58,7 @@ public class DummyValueZeroData extends AbstractData { checkOnEachPublicOperation.checkOnEachPublicOperation(); // Not optimized and creates garbage, because this isn't the primary // use case. Zero data should only be used in bytes form - try { - return getUsing(null); - } catch (Exception e) { - throw zeroReadException(e); - } + return getUsing(null); } @Override @@ -77,9 +73,10 @@ public class DummyValueZeroData extends AbstractData { } private IllegalStateException zeroReadException(Exception cause) { - return new IllegalStateException("Most probable cause of this exception - zero bytes of\n" + + return new IllegalStateException(mh.h().toIdentityString() + + ": Most probable cause of this exception - zero bytes of\n" + "the minimum positive encoding length, supported by the specified or default\n" + "valueSizeMarshaller() is not correct serialized form of any value. You should\n" + - "configure defaultValueProvider() in ChronicleMapBuilder"); + "configure defaultValueProvider() in ChronicleMapBuilder", cause); } } diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/entry/MapEntryStages.java b/src/main/java/net/openhft/chronicle/map/impl/stage/entry/MapEntryStages.java index 55374c57a..a4824eac5 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/entry/MapEntryStages.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/entry/MapEntryStages.java @@ -138,8 +138,8 @@ public abstract class MapEntryStages extends HashEntryStages newValueDoesNotFit: if (newSizeInChunks > entrySizeInChunks) { if (newSizeInChunks > m.maxChunksPerEntry) { - throw new IllegalArgumentException("Value too large: " + - "entry takes " + newSizeInChunks + " chunks, " + + throw new IllegalArgumentException(m.toIdentityString() + + ": Value too large: entry takes " + newSizeInChunks + " chunks, " + m.maxChunksPerEntry + " is maximum."); } if (s.realloc(pos, entrySizeInChunks, newSizeInChunks)) { diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/iter/ReplicatedMapSegmentIteration.java b/src/main/java/net/openhft/chronicle/map/impl/stage/iter/ReplicatedMapSegmentIteration.java index 2c638a66f..7d61115f0 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/iter/ReplicatedMapSegmentIteration.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/iter/ReplicatedMapSegmentIteration.java @@ -146,14 +146,15 @@ public abstract class ReplicatedMapSegmentIteration extends MapSegmentI s.innerWriteLock.unlock(); } } else { - throw new IllegalStateException( - "Entry is present in the map when doInsert() is called"); + throw new IllegalStateException(mh.h().toIdentityString() + + ": Entry is present in the map when doInsert() is called"); } } public void doInsert() { if (mh.set() == null) - throw new IllegalStateException("Called SetAbsentEntry.doInsert() from Map context"); + throw new IllegalStateException(mh.h().toIdentityString() + + ": Called SetAbsentEntry.doInsert() from Map context"); doInsert((Data) DummyValueData.INSTANCE); } } diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapAbsent.java b/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapAbsent.java index 96d48d21a..6e4fdc58a 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapAbsent.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapAbsent.java @@ -75,15 +75,16 @@ public abstract class MapAbsent implements Absent { ks.setSearchState(PRESENT); q.initPresenceOfEntry(EntryPresence.PRESENT); } else { - throw new IllegalStateException( - "Entry is present in the map when doInsert() is called"); + throw new IllegalStateException(mh.h().toIdentityString() + + ": Entry is present in the map when doInsert() is called"); } } @Override public void doInsert() { if (mh.set() == null) - throw new IllegalStateException("Called SetAbsentEntry.doInsert() from Map context"); + throw new IllegalStateException(mh.h().toIdentityString() + + ": Called SetAbsentEntry.doInsert() from Map context"); //noinspection unchecked doInsert((Data) DummyValueData.INSTANCE); } diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapQuery.java b/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapQuery.java index 051a6d62f..15ee373dd 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapQuery.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/query/MapQuery.java @@ -111,8 +111,8 @@ public abstract class MapQuery extends HashQuery ks.setSearchState(PRESENT); initPresenceOfEntry(EntryPresence.PRESENT); } else { - throw new IllegalStateException( - "Entry is absent in the map when doReplaceValue() is called"); + throw new IllegalStateException(mh.h().toIdentityString() + + ": Entry is absent in the map when doReplaceValue() is called"); } } diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapAbsent.java b/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapAbsent.java index 9fbbf0658..ecb5868e7 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapAbsent.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapAbsent.java @@ -59,8 +59,8 @@ public abstract class ReplicatedMapAbsent extends MapAbsent { e.writeEntryPresent(); ru.updateChange(); } else { - throw new IllegalStateException( - "Entry is present in the map when doInsert() is called"); + throw new IllegalStateException(mh.h().toIdentityString() + + ": Entry is present in the map when doInsert() is called"); } } } diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapQuery.java b/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapQuery.java index b6c2bfcb5..54d4b9a16 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapQuery.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/query/ReplicatedMapQuery.java @@ -81,7 +81,8 @@ public abstract class ReplicatedMapQuery extends MapQuery ru.updateChange(); s.tierDeleted(s.tierDeleted() + 1); } else { - throw new IllegalStateException("Entry is absent in the map when doRemove() is called"); + throw new IllegalStateException(mh.h().toIdentityString() + + ": Entry is absent in the map when doRemove() is called"); } } diff --git a/src/main/java/net/openhft/chronicle/map/impl/stage/replication/ReplicationUpdate.java b/src/main/java/net/openhft/chronicle/map/impl/stage/replication/ReplicationUpdate.java index d0a27a8a2..bbaf1469b 100644 --- a/src/main/java/net/openhft/chronicle/map/impl/stage/replication/ReplicationUpdate.java +++ b/src/main/java/net/openhft/chronicle/map/impl/stage/replication/ReplicationUpdate.java @@ -42,10 +42,12 @@ public abstract class ReplicationUpdate implements RemoteOperationContext public void initReplicationUpdate(byte identifier, long timestamp, byte remoteNodeIdentifier) { innerRemoteTimestamp = timestamp; if (identifier == 0) - throw new IllegalStateException("identifier can't be 0"); + throw new IllegalStateException(mh.h().toIdentityString() + ": identifier can't be 0"); innerRemoteIdentifier = identifier; - if (remoteNodeIdentifier == 0) - throw new IllegalStateException("remote node identifier can't be 0"); + if (remoteNodeIdentifier == 0) { + throw new IllegalStateException( + mh.h().toIdentityString() + ": remote node identifier can't be 0"); + } innerRemoteNodeIdentifier = remoteNodeIdentifier; }