blob: 78699d592db5e4d2d4b512ecb47db9fa4a66f001 [file] [log] [blame]
package com.gemstone.gemfire.internal.offheap;
import com.gemstone.gemfire.internal.SharedLibrary;
import com.gemstone.gemfire.pdx.internal.unsafe.UnsafeWrapper;
/**
* Represents a single addressable chunk of off-heap memory. The size specifies
* the number of bytes stored at the address.
*
* @since 9.0
*/
public class UnsafeMemoryChunk implements MemoryChunk {
private static final UnsafeWrapper unsafe;
private static final int ARRAY_BYTE_BASE_OFFSET;
private static String reason;
static {
UnsafeWrapper tmp = null;
try {
tmp = new UnsafeWrapper();
reason = null;
} catch (RuntimeException ignore) {
reason = ignore.toString();
} catch (Error ignore) {
reason = ignore.toString();
}
unsafe = tmp;
ARRAY_BYTE_BASE_OFFSET = unsafe != null ? unsafe.arrayBaseOffset(byte[].class) : 0;
}
private final long data;
private final int size;
public UnsafeMemoryChunk(int size) {
if (unsafe == null) {
throw new OutOfMemoryError("Off-heap memory is not available because: " + reason);
}
try {
this.data = unsafe.allocateMemory(size);
this.size = size;
} catch (OutOfMemoryError err) {
String msg = "Failed creating " + size + " bytes of off-heap memory during cache creation.";
if (err.getMessage() != null && !err.getMessage().isEmpty()) {
msg += " Cause: " + err.getMessage();
}
if (!SharedLibrary.is64Bit() && size >= (1024*1024*1024)) {
msg += " The JVM looks like a 32-bit one. For large amounts of off-heap memory a 64-bit JVM is needed.";
}
throw new OutOfMemoryError(msg);
}
}
public static int getPageSize() {
return unsafe != null ? unsafe.getPageSize() : 8192;
}
@Override
public int getSize() {
return (int)this.size;
}
public long getMemoryAddress() {
return this.data;
}
/**
* Compare the first 'size' bytes at addr1 to those at addr2 and return true
* if they are all equal.
*/
public static boolean compareUnsafeBytes(long addr1, long addr2, int size) {
if ((size >= 8) && ((addr1 & 7) == 0) && ((addr2 & 7) == 0)) {
// We have 8 or more bytes to compare and the addresses are 8 byte aligned
do {
if (unsafe.getLong(null, addr1) != unsafe.getLong(null, addr2)) {
return false;
}
size -= 8;
addr1 += 8;
addr2 += 8;
} while (size >= 8);
}
while (size > 0) {
if (unsafe.getByte(addr1) != unsafe.getByte(addr2)) {
return false;
}
size -= 1;
addr1 += 1;
addr2 += 1;
}
return true;
}
/**
* Like readAbsoluteByte but does no validation of addr
*/
public static byte readUnsafeByte(long addr) {
return unsafe.getByte(addr);
}
/**
* Reads from "addr" to "bytes". Number of bytes read/written is the length of the array.
*/
public static void readUnsafeBytes(long addr, byte[] bytes) {
unsafe.copyMemory(null, addr, bytes, ARRAY_BYTE_BASE_OFFSET, bytes.length);
}
public static byte readAbsoluteByte(long addr) {
return unsafe.getByte(addr);
}
public static char readAbsoluteChar(long addr) {
return unsafe.getChar(null, addr);
}
public static short readAbsoluteShort(long addr) {
return unsafe.getShort(null, addr);
}
public static int readAbsoluteInt(long addr) {
return unsafe.getInt(null, addr);
}
public static int readAbsoluteIntVolatile(long addr) {
return unsafe.getIntVolatile(null, addr);
}
public static long readAbsoluteLong(long addr) {
return unsafe.getLong(null, addr);
}
public static long readAbsoluteLongVolatile(long addr) {
return unsafe.getLongVolatile(null, addr);
}
@Override
public byte readByte(int offset) {
return readAbsoluteByte(this.data+offset);
}
public static void writeAbsoluteByte(long addr, byte value) {
unsafe.putByte(addr, value);
}
public static void writeAbsoluteInt(long addr, int value) {
unsafe.putInt(null, addr, value);
}
public static void writeAbsoluteIntVolatile(long addr, int value) {
unsafe.putIntVolatile(null, addr, value);
}
public static boolean writeAbsoluteIntVolatile(long addr, int expected, int value) {
return unsafe.compareAndSwapInt(null, addr, expected, value);
}
public static void writeAbsoluteLong(long addr, long value) {
unsafe.putLong(null, addr, value);
}
public static void writeAbsoluteLongVolatile(long addr, long value) {
unsafe.putLongVolatile(null, addr, value);
}
public static boolean writeAbsoluteLongVolatile(long addr, long expected, long value) {
return unsafe.compareAndSwapLong(null, addr, expected, value);
}
@Override
public void writeByte(int offset, byte value) {
writeAbsoluteByte(this.data+offset, value);
}
public static void readAbsoluteBytes(long addr, byte[] bytes) {
readAbsoluteBytes(addr, bytes, 0, bytes.length);
}
@Override
public void readBytes(int offset, byte[] bytes) {
readBytes(offset, bytes, 0, bytes.length);
}
public static void writeAbsoluteBytes(long addr, byte[] bytes) {
writeAbsoluteBytes(addr, bytes, 0, bytes.length);
}
public static void clearAbsolute(long addr, long size) {
unsafe.setMemory(addr, size, (byte) 0);
}
@Override
public void writeBytes(int offset, byte[] bytes) {
writeBytes(offset, bytes, 0, bytes.length);
}
public static void readAbsoluteBytes(long addr, byte[] bytes, int bytesOffset, int size) {
// Throwing an Error instead of using the "assert" keyword because passing < 0 to
// copyMemory(...) can lead to a core dump with some JVMs and we don't want to
// require the -ea JVM flag.
if (size < 0) {
throw new AssertionError("Size=" + size + ", but size must be >= 0");
}
assert bytesOffset >= 0 : "byteOffset=" + bytesOffset;
assert bytesOffset + size <= bytes.length : "byteOffset=" + bytesOffset + ",size=" + size + ",bytes.length=" + bytes.length;
if (size == 0) {
return; // No point in wasting time copying 0 bytes
}
unsafe.copyMemory(null, addr, bytes, ARRAY_BYTE_BASE_OFFSET+bytesOffset, size);
}
@Override
public void readBytes(int offset, byte[] bytes, int bytesOffset, int size) {
readAbsoluteBytes(this.data+offset, bytes, bytesOffset, size);
}
public static void copyMemory(long srcAddr, long dstAddr, long size) {
unsafe.copyMemory(srcAddr, dstAddr, size);
}
public static void writeAbsoluteBytes(long addr, byte[] bytes, int bytesOffset, int size) {
// Throwing an Error instead of using the "assert" keyword because passing < 0 to
// copyMemory(...) can lead to a core dump with some JVMs and we don't want to
// require the -ea JVM flag.
if (size < 0) {
throw new AssertionError("Size=" + size + ", but size must be >= 0");
}
assert bytesOffset >= 0 : "byteOffset=" + bytesOffset;
assert bytesOffset + size <= bytes.length : "byteOffset=" + bytesOffset + ",size=" + size + ",bytes.length=" + bytes.length;
if (size == 0) {
return; // No point in wasting time copying 0 bytes
}
unsafe.copyMemory(bytes, ARRAY_BYTE_BASE_OFFSET+bytesOffset, null, addr, size);
}
public static void fill(long addr, int size, byte fill) {
unsafe.setMemory(addr, size, fill);
}
@Override
public void writeBytes(int offset, byte[] bytes, int bytesOffset, int size) {
writeAbsoluteBytes(this.data+offset, bytes, bytesOffset, size);
}
@Override
public void release() {
unsafe.freeMemory(this.data);
}
@Override
public void copyBytes(int src, int dst, int size) {
unsafe.copyMemory(this.data+src, this.data+dst, size);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append("{");
sb.append("MemoryAddress=").append(getMemoryAddress());
sb.append(", Size=").append(getSize());
sb.append("}");
return sb.toString();
}
}