diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPage.java b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPage.java index 12f5831861719..bbaa289b07dc6 100644 --- a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPage.java +++ b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/MuninnPage.java @@ -33,7 +33,7 @@ import static java.lang.String.format; -final class MuninnPage extends OptiLock implements Page +final class MuninnPage extends SequenceLock implements Page { private static final long usageStampOffset = UnsafeUtil.getFieldOffset( MuninnPage.class, "usageStamp" ); diff --git a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/OptiLock.java b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/SequenceLock.java similarity index 87% rename from community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/OptiLock.java rename to community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/SequenceLock.java index 00b6f62c22f44..b2bc5e4474903 100644 --- a/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/OptiLock.java +++ b/community/io/src/main/java/org/neo4j/io/pagecache/impl/muninn/SequenceLock.java @@ -22,9 +22,9 @@ import org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil; /** - * OptiLock is a sequence-based lock like StampedLock, but with the ability to take optimistic write-locks. + * SequenceLock is a sequence-based lock like StampedLock, but with the ability to take optimistic write-locks. *

- * The OptiLock supports non-blocking optimistic concurrent read locks, non-blocking concurrent write locks, + * The SequenceLock supports non-blocking optimistic concurrent read locks, non-blocking concurrent write locks, * and a pessimistic non-blocking exclusive lock. *

* The optimistic read lock works through validation, so at the end of the critical section, the read lock has to be @@ -40,25 +40,30 @@ * The exclusive lock will also invalidate the optimistic read locks, but not the write locks. The exclusive lock is * try-lock only, and will never block. */ -public class OptiLock +public class SequenceLock { - private static final long CNT_BITS = 17; // Bits for counting concurrent write-locks - - private static final long SEQ_BITS = 64 - 1 - CNT_BITS; + // Bits for counting concurrent write-locks. We use 17 bits because our pages are most likely 8192 bytes, and + // 2^17 = 131.072, which is far more than our page size, so makes it highly unlikely that we are going to overflow + // our concurrent write lock counter. Meanwhile, it's also small enough that we have a very large (2^46) number + // space for our sequence. + private static final long CNT_BITS = 17; + + private static final long BITS_IN_LONG = 64; + private static final long SEQ_BITS = BITS_IN_LONG - 1 - CNT_BITS; private static final long CNT_UNIT = 1L << SEQ_BITS; private static final long SEQ_MASK = CNT_UNIT - 1L; private static final long SEQ_IMSK = ~SEQ_MASK; private static final long CNT_MASK = ((1L << CNT_BITS) - 1L) << SEQ_BITS; private static final long EXCL_MASK = (1L << CNT_BITS + SEQ_BITS); - private static final long STATE = UnsafeUtil.getFieldOffset( OptiLock.class, "state" ); + private static final long STATE = UnsafeUtil.getFieldOffset( SequenceLock.class, "state" ); @SuppressWarnings( "unused" ) // accessed via unsafe private volatile long state; private long getState() { - return UnsafeUtil.getLongVolatile( this, STATE ); + return state; } private boolean compareAndSetState( long expect, long update ) @@ -221,9 +226,9 @@ private String describeState( long s ) long excl = s >>> CNT_BITS + SEQ_BITS; long cnt = (s & CNT_MASK) >> SEQ_BITS; long seq = s & SEQ_MASK; - StringBuilder sb = new StringBuilder( "OptiLock[E:" ).append( excl ); - sb.append( ", W:" ).append( Long.toBinaryString( cnt ) ).append( ':' ).append( cnt ); - sb.append( ", S:" ).append( Long.toBinaryString( seq ) ).append( ':' ).append( seq ).append( ']' ); + StringBuilder sb = new StringBuilder( "SequenceLock[Excl: " ).append( excl ); + sb.append( ", Ws: " ).append( cnt ).append( " (" ).append( Long.toBinaryString( cnt ) ); + sb.append( "), S: " ).append( seq ).append( " (" ).append( Long.toBinaryString( seq ) ).append( ")]" ); return sb.toString(); } } diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/OptiLockStressIT.java b/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/SequenceLockStressIT.java similarity index 97% rename from community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/OptiLockStressIT.java rename to community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/SequenceLockStressIT.java index 0529f87898bae..c0d821e1d1422 100644 --- a/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/OptiLockStressIT.java +++ b/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/SequenceLockStressIT.java @@ -34,7 +34,7 @@ import org.neo4j.test.RepeatRule; -public class OptiLockStressIT +public class SequenceLockStressIT { private static final ExecutorService executor = Executors.newCachedThreadPool(new DaemonThreadFactory()); @@ -47,7 +47,7 @@ public static void shutDownExecutor() @Rule public RepeatRule repeatRule = new RepeatRule(); - private OptiLock lock = new OptiLock(); + private SequenceLock lock = new SequenceLock(); @RepeatRule.Repeat( times = 20 ) @Test @@ -210,11 +210,11 @@ protected void doWork() @Test public void thoroughlyEnsureAtomicityOfUnlockExclusiveAndTakeWriteLock() throws Exception { - OptiLockTest test = new OptiLockTest(); + SequenceLockTest test = new SequenceLockTest(); for ( int i = 0; i < 30000; i++ ) { test.unlockExclusiveAndTakeWriteLockMustBeAtomic(); - test.lock = new OptiLock(); + test.lock = new SequenceLock(); } } diff --git a/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/OptiLockTest.java b/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/SequenceLockTest.java similarity index 96% rename from community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/OptiLockTest.java rename to community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/SequenceLockTest.java index d22dec8164eb2..f02b04e5b2521 100644 --- a/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/OptiLockTest.java +++ b/community/io/src/test/java/org/neo4j/io/pagecache/impl/muninn/SequenceLockTest.java @@ -35,7 +35,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -public class OptiLockTest +public class SequenceLockTest { private static final long TIMEOUT = 5000; private static final ExecutorService executor = Executors.newCachedThreadPool(new DaemonThreadFactory()); @@ -46,7 +46,7 @@ public static void shutDownExecutor() executor.shutdown(); } - OptiLock lock = new OptiLock(); + SequenceLock lock = new SequenceLock(); @Test public void uncontendedOptimisticLockMustValidate() throws Exception @@ -341,10 +341,10 @@ public void stampFromUnlockExclusiveMustNotBeValidIfThereAreWriteLocks() throws @Test public void toStringMustDescribeState() throws Exception { - assertThat( lock.toString(), is( "OptiLock[E:0, W:0:0, S:0:0]" ) ); + assertThat( lock.toString(), is( "SequenceLock[Excl: 0, Ws: 0 (0), S: 0 (0)]" ) ); lock.tryWriteLock(); - assertThat( lock.toString(), is( "OptiLock[E:0, W:1:1, S:0:0]" ) ); + assertThat( lock.toString(), is( "SequenceLock[Excl: 0, Ws: 1 (1), S: 0 (0)]" ) ); lock.unlockWrite(); - assertThat( lock.toString(), is( "OptiLock[E:0, W:0:0, S:1:1]" ) ); + assertThat( lock.toString(), is( "SequenceLock[Excl: 0, Ws: 0 (0), S: 1 (1)]" ) ); } } diff --git a/community/primitive-collections/src/main/java/org/neo4j/concurrent/BinaryLatch.java b/community/primitive-collections/src/main/java/org/neo4j/concurrent/BinaryLatch.java index 4353e7209f574..061eeafd6c021 100644 --- a/community/primitive-collections/src/main/java/org/neo4j/concurrent/BinaryLatch.java +++ b/community/primitive-collections/src/main/java/org/neo4j/concurrent/BinaryLatch.java @@ -89,18 +89,6 @@ private void unparkSuccessor( Node waiters ) * This method returns immediately if the latch has already been released. */ public void await() - { - await( this ); - } - - /** - * Wait for the latch to be released, blocking the current thread with the specified blocker, if necessary. - * - * This method returns immediately if the latch has already been released. - * @param blocker The blocker that is given as the reason for blocking the thread, accessible through - * {@link java.util.concurrent.locks.LockSupport#getBlocker(Thread)}. - */ - public void await( Object blocker ) { // Put in a local variable to avoid volatile reads we don't need. Node state = stack; @@ -132,7 +120,7 @@ public void await( Object blocker ) { // Park may wake up spuriously, so we have to loop on it until we observe from the state of the // stack, that the latch has been released. - LockSupport.park( blocker ); + LockSupport.park( this ); } while ( !isReleased( waiter ) ); } diff --git a/community/unsafe/src/main/java/org/neo4j/unsafe/impl/internal/dragons/UnsafeUtil.java b/community/unsafe/src/main/java/org/neo4j/unsafe/impl/internal/dragons/UnsafeUtil.java index 434ccb0ad2232..c2a507971d146 100644 --- a/community/unsafe/src/main/java/org/neo4j/unsafe/impl/internal/dragons/UnsafeUtil.java +++ b/community/unsafe/src/main/java/org/neo4j/unsafe/impl/internal/dragons/UnsafeUtil.java @@ -226,6 +226,9 @@ public static int getAndAddInt( Object obj, long offset, int delta ) return unsafe.getAndAddInt( obj, offset, delta ); } + /** + * Orders loads before the fence, with loads and stores after the fence. + */ public static void loadFence() { unsafe.loadFence();