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 e66cc693f..5d3abef8a 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/BigSegmentHeader.java @@ -24,6 +24,7 @@ import net.openhft.chronicle.core.OS; import java.util.concurrent.TimeUnit; import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; public final class BigSegmentHeader implements SegmentHeader { public static final BigSegmentHeader INSTANCE = new BigSegmentHeader(); @@ -44,9 +45,41 @@ public final class BigSegmentHeader implements SegmentHeader { static final long LOWEST_POSSIBLY_FREE_CHUNK_OFFSET = ENTRIES_OFFSET + 4L; static final long DELETED_OFFSET = LOWEST_POSSIBLY_FREE_CHUNK_OFFSET + 4L; - private static final int TRY_LOCK_NANOS_THRESHOLD = 2_000_000; + /** + * Previously this value was 2 seconds, but GC pauses often take more time that shouldn't + * result to IllegalStateException. + */ + private 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" + + " - 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" + + " process (or one of parallel accessing processes) has crashed while holding\n" + + " this lock. In this case you should use ChronicleMapBuilder.recoverPersistedTo()" + + " procedure\n" + + " to access the Chronicle Map instance.\n" + + " - A concurrent thread or process, currently holding this lock, spends\n" + + " unexpectedly long time (more than " + LOCK_TIMEOUT_SECONDS + " seconds) in\n" + + " the context (try-with-resource block) or one of overridden interceptor\n" + + " methods (or MapMethods, or MapEntryOperations, or MapRemoteOperations)\n" + + " while performing an ordinary Map operation or replication. You should either\n" + + " redesign your logic to spend less time in critical sections (recommended) or\n" + + " acquire this lock with tryLock(time, timeUnit) method call, with sufficient\n" + + " time specified.\n" + + " - Segment(s) in your Chronicle Map are very large, and iteration over them\n" + + " takes more than " + LOCK_TIMEOUT_SECONDS + " seconds. In this case you should\n" + + " acquire this lock with tryLock(time, timeUnit) method call, with longer\n" + + " timeout specified.\n" + + " - This is a dead lock. If you perform multi-key queries, ensure you acquire\n" + + " segment locks in the order (ascending by segmentIndex()), you can find\n" + + " an example here: https://github.com/OpenHFT/Chronicle-Map#multi-key-queries\n"); + } + private static long roundUpNanosToMillis(long nanos) { return NANOSECONDS.toMillis(nanos + 900_000); } @@ -95,14 +128,18 @@ public final class BigSegmentHeader implements SegmentHeader { @Override public void readLock(long address) { - if (!tryReadLock(address, 2, TimeUnit.SECONDS)) { - throw new RuntimeException("Dead lock"); + try { + if (!innerTryReadLock(address, LOCK_TIMEOUT_SECONDS, SECONDS, false)) + throw deadLock(); + } catch (InterruptedException e) { + throw new AssertionError(e); } } @Override - public void readLockInterruptibly(long address) { - readLock(address); + public void readLockInterruptibly(long address) throws InterruptedException { + if (!tryReadLock(address, LOCK_TIMEOUT_SECONDS, SECONDS)) + throw deadLock(); } @Override @@ -111,24 +148,33 @@ public final class BigSegmentHeader implements SegmentHeader { } @Override - public boolean tryReadLock(long address, long time, TimeUnit unit) { - return tryReadLock(address) || tryReadLock0(address, time, unit); + public boolean tryReadLock(long address, long time, TimeUnit unit) throws InterruptedException { + return innerTryReadLock(address, time, unit, true); } - private boolean tryReadLock0(long address, long time, TimeUnit unit) { + private boolean innerTryReadLock(long address, long time, TimeUnit unit, boolean interruptible) + throws InterruptedException { + return tryReadLock(address) || tryReadLock0(address, time, unit, interruptible); + } + + private boolean tryReadLock0(long address, long time, TimeUnit unit, boolean interruptible) + throws InterruptedException { long timeInNanos = unit.toNanos(time); if (timeInNanos < TRY_LOCK_NANOS_THRESHOLD) { - return tryReadLockNanos(address, timeInNanos); + return tryReadLockNanos(address, timeInNanos, interruptible); } else { - return tryReadLockMillis(address, roundUpNanosToMillis(timeInNanos)); + return tryReadLockMillis(address, roundUpNanosToMillis(timeInNanos), interruptible); } } - private boolean tryReadLockNanos(long address, long timeInNanos) { + private boolean tryReadLockNanos(long address, long timeInNanos, boolean interruptible) + throws InterruptedException { long end = System.nanoTime() + timeInNanos; do { if (tryReadLock(address)) return true; + if (interruptible && Thread.interrupted()) + throw new InterruptedException(); } while (System.nanoTime() <= end); return false; } @@ -136,11 +182,14 @@ public final class BigSegmentHeader implements SegmentHeader { /** * Use a timer which is more insensitive to jumps in time like GCs and context switches. */ - private boolean tryReadLockMillis(long address, long timeInMillis) { + private boolean tryReadLockMillis(long address, long timeInMillis, boolean interruptible) + throws InterruptedException { long lastTime = System.currentTimeMillis(); do { if (tryReadLock(address)) return true; + if (interruptible && Thread.interrupted()) + throw new InterruptedException(); long now = System.currentTimeMillis(); if (now != lastTime) { lastTime = now; @@ -162,14 +211,18 @@ public final class BigSegmentHeader implements SegmentHeader { @Override public void updateLock(long address) { - if (!tryUpdateLock(address, 2, TimeUnit.SECONDS)) { - throw new RuntimeException("Dead lock"); + try { + if (!innerTryUpdateLock(address, LOCK_TIMEOUT_SECONDS, SECONDS, false)) + throw deadLock(); + } catch (InterruptedException e) { + throw new AssertionError(e); } } @Override - public void updateLockInterruptibly(long address) { - updateLock(address); + public void updateLockInterruptibly(long address) throws InterruptedException { + if (!tryUpdateLock(address, LOCK_TIMEOUT_SECONDS, SECONDS)) + throw deadLock(); } @Override @@ -178,24 +231,35 @@ public final class BigSegmentHeader implements SegmentHeader { } @Override - public boolean tryUpdateLock(long address, long time, TimeUnit unit) { - return tryUpdateLock(address) || tryUpdateLock0(address, time, unit); + public boolean tryUpdateLock(long address, long time, TimeUnit unit) + throws InterruptedException { + return innerTryUpdateLock(address, time, unit, true); } - private boolean tryUpdateLock0(long address, long time, TimeUnit unit) { + private boolean innerTryUpdateLock( + long address, long time, TimeUnit unit, boolean interruplible) + throws InterruptedException { + return tryUpdateLock(address) || tryUpdateLock0(address, time, unit, interruplible); + } + + private boolean tryUpdateLock0(long address, long time, TimeUnit unit, boolean interruptible) + throws InterruptedException { long timeInNanos = unit.toNanos(time); if (timeInNanos < TRY_LOCK_NANOS_THRESHOLD) { - return tryUpdateLockNanos(address, timeInNanos); + return tryUpdateLockNanos(address, timeInNanos, interruptible); } else { - return tryUpdateLockMillis(address, roundUpNanosToMillis(timeInNanos)); + return tryUpdateLockMillis(address, roundUpNanosToMillis(timeInNanos), interruptible); } } - private boolean tryUpdateLockNanos(long address, long timeInNanos) { + private boolean tryUpdateLockNanos(long address, long timeInNanos, boolean interruptible) + throws InterruptedException { long end = System.nanoTime() + timeInNanos; do { if (tryUpdateLock(address)) return true; + if (interruptible && Thread.interrupted()) + throw new InterruptedException(); } while (System.nanoTime() <= end); return false; } @@ -203,11 +267,14 @@ public final class BigSegmentHeader implements SegmentHeader { /** * Use a timer which is more insensitive to jumps in time like GCs and context switches. */ - private boolean tryUpdateLockMillis(long address, long timeInMillis) { + private boolean tryUpdateLockMillis(long address, long timeInMillis, boolean interruptible) + throws InterruptedException { long lastTime = System.currentTimeMillis(); do { if (tryUpdateLock(address)) return true; + if (interruptible && Thread.interrupted()) + throw new InterruptedException(); long now = System.currentTimeMillis(); if (now != lastTime) { lastTime = now; @@ -219,14 +286,18 @@ public final class BigSegmentHeader implements SegmentHeader { @Override public void writeLock(long address) { - if (!tryWriteLock(address, 2, TimeUnit.SECONDS)) { - throw new RuntimeException("Dead lock"); + try { + if (!innerTryWriteLock(address, LOCK_TIMEOUT_SECONDS, SECONDS, false)) + throw deadLock(); + } catch (InterruptedException e) { + throw new AssertionError(e); } } @Override - public void writeLockInterruptibly(long address) { - writeLock(address); + public void writeLockInterruptibly(long address) throws InterruptedException { + if (!tryWriteLock(address, LOCK_TIMEOUT_SECONDS, SECONDS)) + throw deadLock(); } @Override @@ -235,25 +306,34 @@ public final class BigSegmentHeader implements SegmentHeader { } @Override - public boolean tryWriteLock(long address, long time, TimeUnit unit) { - return tryWriteLock(address) || tryWriteLock0(address, time, unit); + public boolean tryWriteLock(long address, long time, TimeUnit unit) + throws InterruptedException { + return innerTryWriteLock(address, time, unit, true); } - private boolean tryWriteLock0(long address, long time, TimeUnit unit) { + private boolean innerTryWriteLock(long address, long time, TimeUnit unit, boolean interruptible) + throws InterruptedException { + return tryWriteLock(address) || tryWriteLock0(address, time, unit, interruptible); + } + + private boolean tryWriteLock0(long address, long time, TimeUnit unit, boolean interruptible) + throws InterruptedException { long timeInNanos = unit.toNanos(time); if (timeInNanos < TRY_LOCK_NANOS_THRESHOLD) { - return tryWriteLockNanos(address, timeInNanos); + return tryWriteLockNanos(address, timeInNanos, interruptible); } else { - return tryWriteLockMillis(address, roundUpNanosToMillis(timeInNanos)); + return tryWriteLockMillis(address, roundUpNanosToMillis(timeInNanos), interruptible); } } - private boolean tryWriteLockNanos(long address, long timeInNanos) { + private boolean tryWriteLockNanos(long address, long timeInNanos, boolean interruptible) + 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; @@ -262,12 +342,14 @@ public final class BigSegmentHeader implements SegmentHeader { /** * Use a timer which is more insensitive to jumps in time like GCs and context switches. */ - private boolean tryWriteLockMillis(long address, long timeInMillis) { + private boolean tryWriteLockMillis(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; @@ -278,6 +360,14 @@ public final class BigSegmentHeader implements SegmentHeader { return false; } + private void detectInterruptionAndDeregisterWait(long address, boolean interruptible) + throws InterruptedException { + if (interruptible && Thread.interrupted()) { + deregisterWait(address); + throw new InterruptedException(); + } + } + private static void registerWait(long address) { LOCK.registerWait(A, null, address + LOCK_OFFSET); } @@ -288,14 +378,18 @@ public final class BigSegmentHeader implements SegmentHeader { @Override public void upgradeUpdateToWriteLock(long address) { - if (!tryUpgradeUpdateToWriteLock(address, 2, TimeUnit.SECONDS)) { - throw new RuntimeException("Dead lock"); + try { + if (!innerTryUpgradeUpdateToWriteLock(address, LOCK_TIMEOUT_SECONDS, SECONDS, false)) + throw deadLock(); + } catch (InterruptedException e) { + throw new AssertionError(e); } } @Override - public void upgradeUpdateToWriteLockInterruptibly(long address) { - upgradeUpdateToWriteLock(address); + public void upgradeUpdateToWriteLockInterruptibly(long address) throws InterruptedException { + if (!tryUpgradeUpdateToWriteLock(address, LOCK_TIMEOUT_SECONDS, SECONDS)) + throw deadLock(); } @Override @@ -304,26 +398,38 @@ public final class BigSegmentHeader implements SegmentHeader { } @Override - public boolean tryUpgradeUpdateToWriteLock(long address, long time, TimeUnit unit) { - return tryUpgradeUpdateToWriteLock(address) || - tryUpgradeUpdateToWriteLock0(address, time, unit); + public boolean tryUpgradeUpdateToWriteLock(long address, long time, TimeUnit unit) + throws InterruptedException { + return innerTryUpgradeUpdateToWriteLock(address, time, unit, true); } - private boolean tryUpgradeUpdateToWriteLock0(long address, long time, TimeUnit unit) { + private boolean innerTryUpgradeUpdateToWriteLock( + long address, long time, TimeUnit unit, boolean interruptible) + throws InterruptedException { + return tryUpgradeUpdateToWriteLock(address) || + tryUpgradeUpdateToWriteLock0(address, time, unit, interruptible); + } + + private boolean tryUpgradeUpdateToWriteLock0( + long address, long time, TimeUnit unit, boolean interruptible) + throws InterruptedException { long timeInNanos = unit.toNanos(time); if (timeInNanos < TRY_LOCK_NANOS_THRESHOLD) { - return tryUpgradeUpdateToWriteLockNanos(address, timeInNanos); + return tryUpgradeUpdateToWriteLockNanos(address, timeInNanos, interruptible); } else { - return tryUpgradeUpdateToWriteLockMillis(address, roundUpNanosToMillis(timeInNanos)); + return tryUpgradeUpdateToWriteLockMillis( + address, roundUpNanosToMillis(timeInNanos), interruptible); } } - private boolean tryUpgradeUpdateToWriteLockNanos(long address, long timeInNanos) { + private boolean tryUpgradeUpdateToWriteLockNanos( + long address, long timeInNanos, boolean interruptible) throws InterruptedException { long end = System.nanoTime() + timeInNanos; registerWait(address); do { if (LOCK.tryUpgradeUpdateToWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET)) return true; + detectInterruptionAndDeregisterWait(address, interruptible); } while (System.nanoTime() <= end); deregisterWait(address); return false; @@ -332,12 +438,14 @@ public final class BigSegmentHeader implements SegmentHeader { /** * Use a timer which is more insensitive to jumps in time like GCs and context switches. */ - private boolean tryUpgradeUpdateToWriteLockMillis(long address, long timeInMillis) { + private boolean tryUpgradeUpdateToWriteLockMillis( + long address, long timeInMillis, boolean interruptible) throws InterruptedException { long lastTime = System.currentTimeMillis(); registerWait(address); do { if (LOCK.tryUpgradeUpdateToWriteLockAndDeregisterWait(A, null, address + LOCK_OFFSET)) return true; + detectInterruptionAndDeregisterWait(address, interruptible); long now = System.currentTimeMillis(); if (now != lastTime) { lastTime = now; diff --git a/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java b/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java index bed2c9a01..978495076 100644 --- a/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java +++ b/src/main/java/net/openhft/chronicle/hash/impl/SegmentHeader.java @@ -29,27 +29,28 @@ public interface SegmentHeader { void lowestPossiblyFreeChunk(long address, long lowestPossiblyFreeChunk); void readLock(long address); - void readLockInterruptibly(long address); + void readLockInterruptibly(long address) throws InterruptedException; boolean tryReadLock(long address); - boolean tryReadLock(long address, long time, TimeUnit unit); + boolean tryReadLock(long address, long time, TimeUnit unit) throws InterruptedException; void updateLock(long address); - void updateLockInterruptibly(long address); + void updateLockInterruptibly(long address) throws InterruptedException; boolean tryUpdateLock(long address); - boolean tryUpdateLock(long address, long time, TimeUnit unit); + boolean tryUpdateLock(long address, long time, TimeUnit unit) throws InterruptedException; void writeLock(long address); - void writeLockInterruptibly(long address); + void writeLockInterruptibly(long address) throws InterruptedException; boolean tryWriteLock(long address); - boolean tryWriteLock(long address, long time, TimeUnit unit); + boolean tryWriteLock(long address, long time, TimeUnit unit) throws InterruptedException; boolean tryUpgradeReadToUpdateLock(long address); boolean tryUpgradeReadToWriteLock(long address); void upgradeUpdateToWriteLock(long address); - void upgradeUpdateToWriteLockInterruptibly(long address); + void upgradeUpdateToWriteLockInterruptibly(long address) throws InterruptedException; boolean tryUpgradeUpdateToWriteLock(long address); - boolean tryUpgradeUpdateToWriteLock(long address, long time, TimeUnit unit); + boolean tryUpgradeUpdateToWriteLock(long address, long time, TimeUnit unit) + throws InterruptedException; void readUnlock(long address); 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 9241b99d2..3f46fbc36 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 @@ -56,6 +56,8 @@ public class ReadLock implements InterProcessLock { @Override public void lockInterruptibly() throws InterruptedException { checkOnEachPublicOperation.checkOnEachLockOperation(); + if (Thread.interrupted()) + throw new InterruptedException(); if (s.localLockState == UNLOCKED) { if (s.readZero() && s.updateZero() && s.writeZero()) s.segmentHeader.readLockInterruptibly(s.segmentHeaderAddress); @@ -84,6 +86,8 @@ public class ReadLock implements InterProcessLock { @Override public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { checkOnEachPublicOperation.checkOnEachLockOperation(); + if (Thread.interrupted()) + throw new InterruptedException(); if (s.localLockState == UNLOCKED) { if (!s.readZero() || !s.updateZero() || !s.writeZero() || s.segmentHeader.tryReadLock(s.segmentHeaderAddress, time, unit)) { 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 fa6e35d93..3c7b4a00b 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 @@ -90,6 +90,8 @@ public class UpdateLock implements InterProcessLock { @Override public void lockInterruptibly() throws InterruptedException { checkOnEachPublicOperation.checkOnEachLockOperation(); + if (Thread.interrupted()) + throw new InterruptedException(); switch (s.localLockState) { case UNLOCKED: s.checkIterationContextNotLockedInThisThread(); @@ -142,6 +144,8 @@ public class UpdateLock implements InterProcessLock { @Override public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { checkOnEachPublicOperation.checkOnEachLockOperation(); + if (Thread.interrupted()) + throw new InterruptedException(); switch (s.localLockState) { case UNLOCKED: s.checkIterationContextNotLockedInThisThread(); 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 e70312f4d..63ae61af8 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 @@ -94,6 +94,8 @@ public class WriteLock implements InterProcessLock { @Override public void lockInterruptibly() throws InterruptedException { checkOnEachPublicOperation.checkOnEachLockOperation(); + if (Thread.interrupted()) + throw new InterruptedException(); switch (s.localLockState) { case UNLOCKED: s.checkIterationContextNotLockedInThisThread(); @@ -184,6 +186,8 @@ public class WriteLock implements InterProcessLock { @Override public boolean tryLock(long time, @NotNull TimeUnit unit) throws InterruptedException { checkOnEachPublicOperation.checkOnEachLockOperation(); + if (Thread.interrupted()) + throw new InterruptedException(); switch (s.localLockState) { case UNLOCKED: s.checkIterationContextNotLockedInThisThread(); 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 3b56a79b1..3a7b964bc 100644 --- a/src/main/java/net/openhft/chronicle/hash/locks/InterProcessLock.java +++ b/src/main/java/net/openhft/chronicle/hash/locks/InterProcessLock.java @@ -24,22 +24,26 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** - * Abstracts inter-process lock of {@link ChronicleHash} data: segments, entries. + * An inter-process lock, used to control access to some shared off-heap resources of {@link + * ChronicleHash} instances. * *
This lock is not reentrant, but kind of "saturating": multiple {@link #lock()} calls have - * the same effect as a single call. Likewise {@link #unlock()} -- multiple unlocks, or "unlocking" + * the same effect as a single call. Likewise {@link #unlock()} -- multiple unlocks, or unlocking * when the lock isn't actually held, has no negative effects. * *
Once a lock object obtained, it shouldn't be stored in a field and accessed from multiple * threads, instead of that, lock objects should be obtained in each thread separately, using - * the same call chain. This is because since the lock is inter-process, it anyway keeps the - * synchronization state in shared memory, but restricting on-heap "view" of shared lock to a single - * thread is beneficial form performance point-of-view, e. g. fields of the on-heap + * the same call chain. This is because since the lock is inter-process, it anyway keeps it's + * synchronization state in shared off-heap memory, but restricting on-heap "view" of shared lock + * to a single thread is beneficial form performance point-of-view, e. g. fields of the on-heap * {@code InterProcessLock} object shouldn't be {@code volatile}. * *
Lock is inter-process, hence it cannot afford to wait for acquisition infinitely, because * it would be too dead-lock prone. {@link #lock()} throws {@code RuntimeException} after some - * finite time. + * implementation-defined time spent in waiting for the lock acquisition. + * + *
{@code InterProcessLock} supports interruption of lock acquisition (in {@link + * #lockInterruptibly()} and {@link #tryLock(long, TimeUnit)} methods). * * @implNote Inter-process lock is unfair. * @@ -53,13 +57,14 @@ public interface InterProcessLock extends Lock { boolean isHeldByCurrentThread(); /** - * Acquires the lock, if this lock (or stronger-level lock, in the context of {@link - * InterProcessReadWriteUpdateLock}) is already held by current thread, this call returns + * 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. * - *
If the lock is not available then the current thread enter a busy loop. After some - * specified threshold, the thread might be disabled for thread scheduling purposes - * and lies dormant until the lock has been acquired. After another specified threshold + *
If the lock is not available then the current thread enters a busy loop. After some + * 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. * * @throws IllegalMonitorStateException if this method call observes illegal lock state, or some @@ -78,7 +83,7 @@ public interface InterProcessLock extends Lock { * If the lock is not available then this method will return * immediately with the value {@code false}. * - *
Example usage:
{@code
+ * @apiNote Example usage: {@code
* try (ExternalMapQueryContext q = map.queryContext(key)) {
* if (q.updateLock().tryLock()) {
* // highly-probable branch