Skip to content

Commit

Permalink
Update new fields access to use unsafe only, tests for newly
Browse files Browse the repository at this point in the history
introduced helper methods
  • Loading branch information
MishaDemianenko committed Jan 23, 2018
1 parent eab3a3b commit 1754caa
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 36 deletions.
Expand Up @@ -51,7 +51,7 @@ final class MuninnPage extends SequenceLock implements Page
private long pointer;

// max transaction id that updated this page
@SuppressWarnings( "unused" )
@SuppressWarnings( "unused" ) // accessed using unsafe
private volatile long lastModifiedTxId = UNBOUND_LAST_MODIFIED_TX_ID;

// Optimistically incremented; occasionally truncated to a max of 4.
Expand Down Expand Up @@ -91,30 +91,22 @@ public long address()
return pointer;
}

public long getLastModifiedTxId()
long getLastModifiedTxId()
{
return lastModifiedTxId;
return UnsafeUtil.getLongVolatile( this, lastModifiedTxIdOffset );
}

public long resetLastModifiedTxId()
/**
* @return return current value of {@link #lastModifiedTxId} and resets it to {@link #UNBOUND_LAST_MODIFIED_TX_ID}
*/
long getAndResetLastModifiedTransactionId()
{
long value = lastModifiedTxId;
lastModifiedTxId = UNBOUND_LAST_MODIFIED_TX_ID;
return value;
return UnsafeUtil.getAndSetLong( this, lastModifiedTxIdOffset, UNBOUND_LAST_MODIFIED_TX_ID );
}

public void setLastModifiedTxId( long modifierTxId )
void setLastModifiedTxId( long modifierTxId )
{
long pageLatestModifier;
do
{
pageLatestModifier = this.lastModifiedTxId;
if ( pageLatestModifier >= modifierTxId )
{
return;
}
}
while ( !UnsafeUtil.compareAndSwapLong( this, lastModifiedTxIdOffset, pageLatestModifier, modifierTxId ) );
UnsafeUtil.compareAndSetMaxLong( this, lastModifiedTxIdOffset, modifierTxId );
}

/**
Expand Down
Expand Up @@ -131,17 +131,31 @@ public final boolean next( long pageId ) throws IOException
void verifyContext()
{
VersionContext versionContext = versionContextSupplier.getVersionContext();
if ( versionContext.lastClosedTransactionId() == Long.MAX_VALUE )
long lastClosedTransactionId = versionContext.lastClosedTransactionId();
if ( lastClosedTransactionId == Long.MAX_VALUE )
{
return;
}
if ( page.getLastModifiedTxId() > versionContext.lastClosedTransactionId() ||
pagedFile.getHighestEvictedTransactionId() > versionContext.lastClosedTransactionId() )
if ( isPotentiallyReadingDirtyData( lastClosedTransactionId ) )
{
versionContext.markAsDirty();
}
}

/**
* We reading potentially dirty data in case if our page last modification version is higher then
* requested lastClosedTransactionId; or for this page file we already evict some page with version that is higher
* then requested lastClosedTransactionId. In this case we can't be sure that data of current page satisfying
* visibility requirements and we pessimistically will assume that we reading dirty data.
* @param lastClosedTransactionId last closed transaction id
* @return true in case if we reading potentially dirty data for requested lastClosedTransactionId.
*/
private boolean isPotentiallyReadingDirtyData( long lastClosedTransactionId )
{
return page.getLastModifiedTxId() > lastClosedTransactionId ||
pagedFile.getHighestEvictedTransactionId() > lastClosedTransactionId;
}

@Override
public final void close()
{
Expand Down
Expand Up @@ -38,6 +38,6 @@ public void onEvict( long filePageId, Page page )
assert removed == page :
"Removed unexpected page when cleaning up translation table for filePageId " + filePageId + ". " +
"Evicted " + page + " but removed " + removed + " from the translation table.";
file.setHighestEvictedTransactionId( removed.resetLastModifiedTxId() );
file.setHighestEvictedTransactionId( removed.getAndResetLastModifiedTransactionId() );
}
}
Expand Up @@ -77,6 +77,7 @@ final class MuninnPagedFile implements PagedFile, Flushable

// max modifier transaction id among evicted pages for this file
private static final long evictedTransactionIdOffset = UnsafeUtil.getFieldOffset( MuninnPagedFile.class, "highestEvictedTransactionId" );
@SuppressWarnings( "unused" ) // accessed using unsafe
private volatile long highestEvictedTransactionId;

/**
Expand Down Expand Up @@ -612,21 +613,12 @@ MuninnPage evictPage( long filePageId )

void setHighestEvictedTransactionId( long modifiedTransactionId )
{
long highestModifier;
do
{
highestModifier = this.highestEvictedTransactionId;
if ( highestModifier >= modifiedTransactionId )
{
return;
}
}
while ( !UnsafeUtil.compareAndSwapLong( this, evictedTransactionIdOffset, highestModifier, modifiedTransactionId ) );
UnsafeUtil.compareAndSetMaxLong( this, evictedTransactionIdOffset, modifiedTransactionId );
}

long getHighestEvictedTransactionId()
{
return highestEvictedTransactionId;
return UnsafeUtil.getLongVolatile( this, evictedTransactionIdOffset );
}

/**
Expand Down
Expand Up @@ -307,6 +307,32 @@ public static Object getAndSetObject( Object obj, long offset, Object newValue )
return unsafe.getAndSetObject( obj, offset, newValue );
}

/**
* Atomically exchanges provided <code>newValue</code> with the current value of field or array element, with
* provided <code>offset</code>.
*/
public static long getAndSetLong( Object object, long offset, long newValue )
{
return unsafe.getAndSetLong( object, offset, newValue );
}

/**
* Atomically set field or array element to a maximum between current value and provided <code>newValue</code>
*/
public static void compareAndSetMaxLong( Object object, long fieldOffset, long newValue )
{
long currentValue;
do
{
currentValue = UnsafeUtil.getLong( object, fieldOffset );
if ( currentValue >= newValue )
{
return;
}
}
while ( !UnsafeUtil.compareAndSwapLong( object, fieldOffset, currentValue, newValue ) );
}

/**
* Create a string with a char[] that you know is not going to be modified, so avoid the copy constructor.
*
Expand Down
Expand Up @@ -24,6 +24,8 @@
import java.nio.ByteBuffer;
import java.util.Objects;

import static java.lang.System.currentTimeMillis;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isOneOf;
import static org.hamcrest.Matchers.not;
Expand All @@ -33,9 +35,59 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static java.lang.System.currentTimeMillis;

import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.*;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.allocateMemory;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.arrayBaseOffset;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.arrayIndexScale;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.arrayOffset;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.assertHasUnsafe;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.compareAndSetMaxLong;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.compareAndSwapLong;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.compareAndSwapObject;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.free;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getAndAddInt;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getAndSetLong;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getAndSetObject;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getBoolean;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getBooleanVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getByte;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getByteVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getChar;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getCharVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getDouble;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getDoubleVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getFieldOffset;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getFloat;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getFloatVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getInt;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getIntVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getLong;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getLongVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getObject;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getObjectVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getShort;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.getShortVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.initDirectByteBuffer;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.newDirectByteBuffer;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.pageSize;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putBoolean;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putBooleanVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putByte;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putByteVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putChar;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putCharVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putDouble;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putDoubleVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putFloat;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putFloatVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putInt;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putIntVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putLong;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putLongVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putObject;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putObjectVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putShort;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.putShortVolatile;
import static org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.setMemory;

public class UnsafeUtilTest
{
Expand Down Expand Up @@ -347,6 +399,29 @@ public void getAndSetObjectField() throws Exception
assertThat( obj, is( new Obj() ) );
}

@Test
public void getAndSetLongField()
{
Obj obj = new Obj();
long offset = getFieldOffset( Obj.class, "aLong" );
assertThat( getAndSetLong( obj, offset, 42L ), equalTo( 0L ) );
assertThat( getAndSetLong( obj, offset, -1 ), equalTo( 42L ) );
}

@Test
public void compareAndSetMaxLongField()
{
Obj obj = new Obj();
long offset = getFieldOffset( Obj.class, "aLong" );
assertThat( getAndSetLong( obj, offset, 42L ), equalTo( 0L ) );

compareAndSetMaxLong( obj, offset, 5 );
assertEquals( 42, getLong( obj, offset ) );

compareAndSetMaxLong( obj, offset, 105 );
assertEquals( 105, getLong( obj, offset ) );
}

@Test
public void unsafeArrayElementAccess() throws Exception
{
Expand Down

0 comments on commit 1754caa

Please sign in to comment.