Skip to content

Commit

Permalink
Rename OptiLock to SequenceLock and other small cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvest committed Jan 20, 2016
1 parent 49296c0 commit e2f1a84
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 34 deletions.
Expand Up @@ -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" );

Expand Down
Expand Up @@ -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.
* <p>
* 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.
* <p>
* The optimistic read lock works through validation, so at the end of the critical section, the read lock has to be
Expand All @@ -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 )
Expand Down Expand Up @@ -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();
}
}
Expand Up @@ -34,7 +34,7 @@

import org.neo4j.test.RepeatRule;

public class OptiLockStressIT
public class SequenceLockStressIT
{
private static final ExecutorService executor = Executors.newCachedThreadPool(new DaemonThreadFactory());

Expand All @@ -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
Expand Down Expand Up @@ -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();
}

}
Expand Down
Expand Up @@ -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());
Expand All @@ -46,7 +46,7 @@ public static void shutDownExecutor()
executor.shutdown();
}

OptiLock lock = new OptiLock();
SequenceLock lock = new SequenceLock();

@Test
public void uncontendedOptimisticLockMustValidate() throws Exception
Expand Down Expand Up @@ -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)]" ) );
}
}
Expand Up @@ -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;
Expand Down Expand Up @@ -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 ) );
}
Expand Down
Expand Up @@ -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();
Expand Down

0 comments on commit e2f1a84

Please sign in to comment.