All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil Maven / Gradle / Ivy

There is a newer version: 5.26.1
Show newest version
/*
 * Copyright (c) 2002-2016 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.unsafe.impl.internal.dragons;

import sun.misc.Unsafe;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

import static org.neo4j.unsafe.impl.internal.dragons.FeatureToggles.flag;

/**
 * Always check that the Unsafe utilities are available with the {@link UnsafeUtil#assertHasUnsafe} method, before
 * calling any of the other methods.
 * 

* Avoid `import static` for these individual methods. Always qualify method usages with `UnsafeUtil` so use sites * show up in code greps. */ public final class UnsafeUtil { /** * Whether or not to explicitly dirty the allocated memory. This is off by default. * The {@link UnsafeUtil#allocateMemory(long)} method is not guaranteed to allocate zeroed out memory, but might * often do so by pure chance. * Enabling this feature will make sure that the allocated memory is full of random data, such that we can test * and verify that our code does not assume that memory is clean when allocated. */ private static final boolean DIRTY_MEMORY = flag( UnsafeUtil.class, "DIRTY_MEMORY", false ); private static final Unsafe unsafe; private static final MethodHandle sharedStringConstructor; private static final String allowUnalignedMemoryAccessProperty = "org.neo4j.unsafe.impl.internal.dragons.UnsafeUtil.allowUnalignedMemoryAccess"; private static final Class directByteBufferClass; private static final Constructor directByteBufferCtor; private static final long directByteBufferMarkOffset; private static final long directByteBufferPositionOffset; private static final long directByteBufferLimitOffset; private static final long directByteBufferCapacityOffset; private static final long directByteBufferAddressOffset; private static final int pageSize; public static final boolean allowUnalignedMemoryAccess; public static final boolean storeByteOrderIsNative; static { unsafe = getUnsafe(); MethodHandles.Lookup lookup = MethodHandles.lookup(); sharedStringConstructor = getSharedStringConstructorMethodHandle( lookup ); Class dbbClass = null; Constructor ctor = null; long dbbMarkOffset = 0; long dbbPositionOffset = 0; long dbbLimitOffset = 0; long dbbCapacityOffset = 0; long dbbAddressOffset = 0; int ps = 4096; try { dbbClass = Class.forName( "java.nio.DirectByteBuffer" ); Class bufferClass = Class.forName( "java.nio.Buffer" ); dbbMarkOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "mark" ) ); dbbPositionOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "position" ) ); dbbLimitOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "limit" ) ); dbbCapacityOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "capacity" ) ); dbbAddressOffset = unsafe.objectFieldOffset( bufferClass.getDeclaredField( "address" ) ); ps = unsafe.pageSize(); } catch ( Throwable e ) { if ( dbbClass == null ) { throw new LinkageError( "Cannot to link java.nio.DirectByteBuffer", e ); } try { ctor = dbbClass.getConstructor( Long.TYPE, Integer.TYPE ); ctor.setAccessible( true ); } catch ( NoSuchMethodException e1 ) { throw new LinkageError( "Cannot find JNI constructor for java.nio.DirectByteBuffer", e1 ); } } directByteBufferClass = dbbClass; directByteBufferCtor = ctor; directByteBufferMarkOffset = dbbMarkOffset; directByteBufferPositionOffset = dbbPositionOffset; directByteBufferLimitOffset = dbbLimitOffset; directByteBufferCapacityOffset = dbbCapacityOffset; directByteBufferAddressOffset = dbbAddressOffset; pageSize = ps; // See java.nio.Bits.unaligned() and its uses. String alignmentProperty = System.getProperty( allowUnalignedMemoryAccessProperty ); if ( alignmentProperty != null && (alignmentProperty.equalsIgnoreCase( "true" ) || alignmentProperty.equalsIgnoreCase( "false" )) ) { allowUnalignedMemoryAccess = Boolean.parseBoolean( alignmentProperty ); } else { boolean unaligned = false; String arch = System.getProperty( "os.arch", "?" ); switch ( arch ) // list of architectures that support unaligned access to memory { case "x86_64": case "i386": case "x86": case "amd64": case "ppc64": case "ppc64le": case "ppc64be": unaligned = true; } allowUnalignedMemoryAccess = unaligned; } storeByteOrderIsNative = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; } private static Unsafe getUnsafe() { try { PrivilegedExceptionAction getUnsafe = () -> { try { return Unsafe.getUnsafe(); } catch ( Exception e ) { Class type = Unsafe.class; Field[] fields = type.getDeclaredFields(); for ( Field field : fields ) { if ( Modifier.isStatic( field.getModifiers() ) && type.isAssignableFrom( field.getType() ) ) { field.setAccessible( true ); return type.cast( field.get( null ) ); } } LinkageError error = new LinkageError( "No static field of type sun.misc.Unsafe" ); error.addSuppressed( e ); throw error; } }; return AccessController.doPrivileged( getUnsafe ); } catch ( Exception e ) { throw new LinkageError( "Cannot access sun.misc.Unsafe", e ); } } /** * @throws java.lang.LinkageError if the Unsafe tools are not available on in this JVM. */ public static void assertHasUnsafe() { if ( unsafe == null ) { throw new LinkageError( "Unsafe not available" ); } } private static MethodHandle getSharedStringConstructorMethodHandle( MethodHandles.Lookup lookup ) { try { Constructor constructor = String.class.getDeclaredConstructor( char[].class, Boolean.TYPE ); constructor.setAccessible( true ); return lookup.unreflectConstructor( constructor ); } catch ( Exception e ) { return null; } } /** * Get the object-relative field offset. */ public static long getFieldOffset( Class type, String field ) { try { return unsafe.objectFieldOffset( type.getDeclaredField( field ) ); } catch ( NoSuchFieldException e ) { String message = "Could not get offset of '" + field + "' field on type " + type; throw new LinkageError( message, e ); } } /** * Atomically add the given delta to the int field, and return its previous value. *

* This has the memory visibility semantics of a volatile read followed by a volatile write. */ 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(); } /** * Atomically compare the current value of the given long field with the expected value, and if they are the * equal, set the field to the updated value and return true. Otherwise return false. *

* If this method returns true, then it has the memory visibility semantics of a volatile read followed by a * volatile write. */ public static boolean compareAndSwapLong( Object obj, long offset, long expected, long update ) { return unsafe.compareAndSwapLong( obj, offset, expected, update ); } /** * Same as compareAndSwapLong, but for object references. */ public static boolean compareAndSwapObject( Object obj, long offset, Object expected, Object update ) { return unsafe.compareAndSwapObject( obj, offset, expected, update ); } /** * Same as getAndAddInt, but for object references. */ public static Object getAndSetObject( Object obj, long offset, Object newValue ) { return unsafe.getAndSetObject( obj, offset, newValue ); } /** * Create a string with a char[] that you know is not going to be modified, so avoid the copy constructor. * * @param chars array that will back the new string * @return the created string */ public static String newSharedArrayString( char[] chars ) { if ( sharedStringConstructor != null ) { try { return (String) sharedStringConstructor.invokeExact( chars, true ); } catch ( Throwable throwable ) { throw new LinkageError( "Unexpected 'String constructor' intrinsic failure", throwable ); } } else { return new String( chars ); } } /** * Allocate a slab of memory of the given size in bytes, and return a pointer to that memory. *

* The memory is aligned such that it can be used for any data type. The memory is cleared, so all bytes are zero. */ public static long allocateMemory( long sizeInBytes ) { final long pointer = unsafe.allocateMemory( sizeInBytes ); if ( DIRTY_MEMORY ) { setMemory( pointer, sizeInBytes, (byte) 0xA5 ); } return pointer; } /** * Free the memory that was allocated with {@link #allocateMemory}. */ public static void free( long pointer ) { unsafe.freeMemory( pointer ); } /** * Return the power-of-2 native memory page size. */ public static int pageSize() { return pageSize; } public static void putBoolean( Object obj, long offset, boolean value ) { unsafe.putBoolean( obj, offset, value ); } public static boolean getBoolean( Object obj, long offset ) { return unsafe.getBoolean( obj, offset ); } public static void putBooleanVolatile( Object obj, long offset, boolean value ) { unsafe.putBooleanVolatile( obj, offset, value ); } public static boolean getBooleanVolatile( Object obj, long offset ) { return unsafe.getBooleanVolatile( obj, offset ); } public static void putByte( long address, byte value ) { unsafe.putByte( address, value ); } public static byte getByte( long address ) { return unsafe.getByte( address ); } public static void putByteVolatile( long address, byte value ) { unsafe.putByteVolatile( null, address, value ); } public static byte getByteVolatile( long address ) { return unsafe.getByteVolatile( null, address ); } public static void putByte( Object obj, long offset, byte value ) { unsafe.putByte( obj, offset, value ); } public static byte getByte( Object obj, long offset ) { return unsafe.getByte( obj, offset ); } public static byte getByteVolatile( Object obj, long offset ) { return unsafe.getByteVolatile( obj, offset ); } public static void putByteVolatile( Object obj, long offset, byte value ) { unsafe.putByteVolatile( obj, offset, value ); } public static void putShort( long address, short value ) { unsafe.putShort( address, value ); } public static short getShort( long address ) { return unsafe.getShort( address ); } public static void putShortVolatile( long address, short value ) { unsafe.putShortVolatile( null, address, value ); } public static short getShortVolatile( long address ) { return unsafe.getShortVolatile( null, address ); } public static void putShort( Object obj, long offset, short value ) { unsafe.putShort( obj, offset, value ); } public static short getShort( Object obj, long offset ) { return unsafe.getShort( obj, offset ); } public static void putShortVolatile( Object obj, long offset, short value ) { unsafe.putShortVolatile( obj, offset, value ); } public static short getShortVolatile( Object obj, long offset ) { return unsafe.getShortVolatile( obj, offset ); } public static void putFloat( long address, float value ) { unsafe.putFloat( address, value ); } public static float getFloat( long address ) { return unsafe.getFloat( address ); } public static void putFloatVolatile( long address, float value ) { unsafe.putFloatVolatile( null, address, value ); } public static float getFloatVolatile( long address ) { return unsafe.getFloatVolatile( null, address ); } public static void putFloat( Object obj, long offset, float value ) { unsafe.putFloat( obj, offset, value ); } public static float getFloat( Object obj, long offset ) { return unsafe.getFloat( obj, offset ); } public static void putFloatVolatile( Object obj, long offset, float value ) { unsafe.putFloatVolatile( obj, offset, value ); } public static float getFloatVolatile( Object obj, long offset ) { return unsafe.getFloatVolatile( obj, offset ); } public static void putChar( long address, char value ) { unsafe.putChar( address, value ); } public static char getChar( long address ) { return unsafe.getChar( address ); } public static void putCharVolatile( long address, char value ) { unsafe.putCharVolatile( null, address, value ); } public static char getCharVolatile( long address ) { return unsafe.getCharVolatile( null, address ); } public static void putChar( Object obj, long offset, char value ) { unsafe.putChar( obj, offset, value ); } public static char getChar( Object obj, long offset ) { return unsafe.getChar( obj, offset ); } public static void putCharVolatile( Object obj, long offset, char value ) { unsafe.putCharVolatile( obj, offset, value ); } public static char getCharVolatile( Object obj, long offset ) { return unsafe.getCharVolatile( obj, offset ); } public static void putInt( long address, int value ) { unsafe.putInt( address, value ); } public static int getInt( long address ) { return unsafe.getInt( address ); } public static void putIntVolatile( long address, int value ) { unsafe.putIntVolatile( null, address, value ); } public static int getIntVolatile( long address ) { return unsafe.getIntVolatile( null, address ); } public static void putInt( Object obj, long offset, int value ) { unsafe.putInt( obj, offset, value ); } public static int getInt( Object obj, long offset ) { return unsafe.getInt( obj, offset ); } public static void putIntVolatile( Object obj, long offset, int value ) { unsafe.putIntVolatile( obj, offset, value ); } public static int getIntVolatile( Object obj, long offset ) { return unsafe.getIntVolatile( obj, offset ); } public static void putLongVolatile( long address, long value ) { unsafe.putLongVolatile( null, address, value ); } public static long getLongVolatile( long address ) { return unsafe.getLongVolatile( null, address ); } public static void putLong( long address, long value ) { unsafe.putLong( address, value ); } public static long getLong( long address ) { return unsafe.getLong( address ); } public static void putLong( Object obj, long offset, long value ) { unsafe.putLong( obj, offset, value ); } public static long getLong( Object obj, long offset ) { return unsafe.getLong( obj, offset ); } public static void putLongVolatile( Object obj, long offset, long value ) { unsafe.putLongVolatile( obj, offset, value ); } public static long getLongVolatile( Object obj, long offset ) { return unsafe.getLongVolatile( obj, offset ); } public static void putDouble( long address, double value ) { unsafe.putDouble( address, value ); } public static double getDouble( long address ) { return unsafe.getDouble( address ); } public static void putDoubleVolatile( long address, double value ) { unsafe.putDoubleVolatile( null, address, value ); } public static double getDoubleVolatile( long address ) { return unsafe.getDoubleVolatile( null, address ); } public static void putDouble( Object obj, long offset, double value ) { unsafe.putDouble( obj, offset, value ); } public static double getDouble( Object obj, long offset ) { return unsafe.getDouble( obj, offset ); } public static void putDoubleVolatile( Object obj, long offset, double value ) { unsafe.putDoubleVolatile( obj, offset, value ); } public static double getDoubleVolatile( Object obj, long offset ) { return unsafe.getDoubleVolatile( obj, offset ); } public static void putObject( Object obj, long offset, Object value ) { unsafe.putObject( obj, offset, value ); } public static Object getObject( Object obj, long offset ) { return unsafe.getObject( obj, offset ); } public static Object getObjectVolatile( Object obj, long offset ) { return unsafe.getObjectVolatile( obj, offset ); } public static void putObjectVolatile( Object obj, long offset, Object value ) { unsafe.putObjectVolatile( obj, offset, value ); } public static int arrayBaseOffset( Class klass ) { return unsafe.arrayBaseOffset( klass ); } public static int arrayIndexScale( Class klass ) { int scale = unsafe.arrayIndexScale( klass ); if ( scale == 0 ) { throw new AssertionError( "Array type too narrow for unsafe access: " + klass ); } return scale; } public static int arrayOffset( int index, int base, int scale ) { return base + index * scale; } /** * Set the given number of bytes to the given value, starting from the given address. */ public static void setMemory( long address, long bytes, byte value ) { unsafe.setMemory( address, bytes, value ); } /** * Copy the given number of bytes from the source address to the destination address. */ public static void copyMemory( long srcAddress, long destAddress, long bytes ) { unsafe.copyMemory( srcAddress, destAddress, bytes ); } /** * Create a new DirectByteBuffer that wraps the given address and has the given capacity. *

* The ByteBuffer does NOT create a Cleaner, or otherwise register the pointer for freeing. */ public static ByteBuffer newDirectByteBuffer( long addr, int cap ) throws Exception { if ( directByteBufferCtor == null ) { // Simulate the JNI NewDirectByteBuffer(void*, long) invocation. Object dbb = unsafe.allocateInstance( directByteBufferClass ); initDirectByteBuffer( dbb, addr, cap ); return (ByteBuffer) dbb; } // Reflection based fallback code. return (ByteBuffer) directByteBufferCtor.newInstance( addr, cap ); } /** * Initialize (simulate calling the constructor of) the given DirectByteBuffer. */ public static void initDirectByteBuffer( Object dbb, long addr, int cap ) { unsafe.putInt( dbb, directByteBufferMarkOffset, -1 ); unsafe.putInt( dbb, directByteBufferPositionOffset, 0 ); unsafe.putInt( dbb, directByteBufferLimitOffset, cap ); unsafe.putInt( dbb, directByteBufferCapacityOffset, cap ); unsafe.putLong( dbb, directByteBufferAddressOffset, addr ); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy