/** * Copyright 2013, Landz and its contributors. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package z.util; import sun.misc.Unsafe; import z.annotation.NotThreadSafe; import java.lang.reflect.Field; import static z.util.Throwables.uncheckTo; /** * All APIs to expose Unsafe side things in Hotspot for use. * * @Performance, we keep all exposed publics in constant as possible */ public class Unsafes { public static final int SIZE_BYTE_TYPE = Byte.BYTES; public static final int SIZE_INT_TYPE = Integer.BYTES; public static final int SIZE_SHORT_TYPE = Short.BYTES; public static final int SIZE_CHAR_TYPE = Character.BYTES; public static final int SIZE_LONG_TYPE = Long.BYTES; public static final int SIZE_FLOAT_TYPE = Float.BYTES; public static final int SIZE_DOUBLE_TYPE = Double.BYTES; public static final Unsafe UNSAFE = uncheckTo(() -> { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(null); }); static final boolean is64bit = true; // auto detect if possible.public static void printAddresses(java.lang.String label, java.lang.Object... objects) { public static void printAddresses(String label, Object... objects) { System.out.print(label + ": 0x"); long last = 0; int offset = UNSAFE.arrayBaseOffset(objects.getClass()); int scale = UNSAFE.arrayIndexScale(objects.getClass()); switch (scale) { case 4: long factor = is64bit ? 8 : 1; final long i1 = (UNSAFE.getInt(objects, offset) & 0xFFFFFFFFL) * factor; System.out.print(Long.toHexString(i1)); last = i1; for (int i = 1; i < objects.length; i++) { final long i2 = (UNSAFE.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor; if (i2 > last) System.out.print(", +" + Long.toHexString(i2 - last)); else System.out.print(", -" + Long.toHexString(last - i2)); last = i2; } break; case 8: throw new AssertionError("Not supported"); } System.out.println(); } /**the size (in bytes) of a native memory page. */ public static final int SIZE_PAGE = UNSAFE.pageSize(); public static final boolean isPageAligned(long address) { return (address & (SIZE_PAGE-1))==0; } /** * return the next page aligned address no matter the current address is * aligned or not. <p> * @param address * @return */ public static final long nextPageAlignedAddress(long address) { return address-(address&(SIZE_PAGE-1))+SIZE_PAGE; } /** * return the next and nearest page aligned address. If the address has * already aligned, return input address itself. * <p> * see aslo {@link #nextPageAlignedAddress} * @param address * @return */ public static final long nextNearestPageAlignedAddress(long address) { long masked = address&(SIZE_PAGE-1); if (masked!=0) { return address-masked+SIZE_PAGE; } else { return address; } } /** NOTE: SIZE_CACHE_LINE is CPU dependent, now for x86-64/x86. */ public static final int SIZE_CACHE_LINE = 64; /** NOTE: Doubling for modern adjacent cache-line prefetch. */ public static final int SIZE_CACHE_LINE_PADDING = 2*SIZE_CACHE_LINE; private static final int cacheLineSize() { return 64; } public static final boolean isCacheLineAligned(long address) { return (address&(SIZE_CACHE_LINE-1))==0; } public static final long nextCacheLineAlignedAddress(long address) { return address-(address&(SIZE_CACHE_LINE-1))+SIZE_CACHE_LINE; } public static final long nextNearestCacheLineAlignedAddress(long address) { long masked = address&(SIZE_CACHE_LINE-1); if (masked!=0) { return address-masked+SIZE_CACHE_LINE; } else { return address; } } /** The value of addressSize */ public static final int SIZE_ADDRESS = UNSAFE.ADDRESS_SIZE; public static final long nextMachineWordAlignedAddress(long address) { return address-(address&(SIZE_ADDRESS-1))+SIZE_ADDRESS; } public static final long nextNearestMachineWordAlignedAddress(long address) { long masked = address&(SIZE_ADDRESS-1); if (masked!=0) { return address-masked+SIZE_ADDRESS; } else { return address; } } static long address(Object obj) { Object[] array = new Object[]{obj}; long baseOffset = UNSAFE.arrayBaseOffset(Object[].class); return UNSAFE.getInt(array, baseOffset); } public static boolean isArchIntel64() { String arch = SystemProperty.OS_ARCH.value(); return arch.equals("amd64") || arch.equals("x86_64"); } public static boolean isArchIntel32() { String arch = SystemProperty.OS_ARCH.value(); return arch.equals("i386") || arch.equals("x86"); } /** * Note: here x86 arch includes x86 and x86-64 * @return */ public static boolean isArchX86() { String arch = SystemProperty.OS_ARCH.value(); return arch.equals("i386") || arch.equals("x86") || arch.equals("amd64") || arch.equals("x86_64"); } /** * a helper on {@link sun.misc.Unsafe#setMemory} to zero the offheap buffer. * @param addressOfBuffer * @param sizeOfBytes */ public static void systemClearMemory(long addressOfBuffer, int sizeOfBytes) { switch (sizeOfBytes) { case 1 : UNSAFE.putByte(addressOfBuffer,(byte)0); break; case 2: UNSAFE.putShort(addressOfBuffer, (short) 0); break; case 4: UNSAFE.putInt(addressOfBuffer, 0); break; case 8: UNSAFE.putLong(addressOfBuffer, 0); break; case 16: UNSAFE.putLong(addressOfBuffer, 0); UNSAFE.putLong(addressOfBuffer+8,0); break; case 32: UNSAFE.putLong(addressOfBuffer,0); UNSAFE.putLong(addressOfBuffer+8,0); UNSAFE.putLong(addressOfBuffer+16,0); UNSAFE.putLong(addressOfBuffer+24,0); break; default: UNSAFE.setMemory(addressOfBuffer, sizeOfBytes, (byte)0); } } /** * Allocates a new block of native memory, of the given size in byte unit. * The contents of the memory are uninitialized; they will generally be * garbage. The resulting native pointer will never be zero, and will be * aligned for all value types. * * @throws IllegalArgumentException if the size is negative or too large * for the native size_t type * * @throws OutOfMemoryError if the allocation is refused by the system */ public static long systemAllocateMemory(long sizeOfBytes) { return UNSAFE.allocateMemory(sizeOfBytes); } /** * Disposes of a block of native memory, as obtained from {@link * #systemAllocateMemory}. The currentAddress passed to this method may be null, in which * case no action is taken. * * @see #systemAllocateMemory */ public static void systemFreeMemory(long address) { UNSAFE.freeMemory(address); } //====================================================================== // DSL for currentAddress /** * NOTE: <p> * for support friendly DSL style, we still need OnAddress instance... * * XXX: is @NotThreadSafe OK? */ @NotThreadSafe private static class OnAddressImpl implements OnAddress, OnAddressFollowBy { private OnAddressImpl() {} private long startAddress = 0L; private long currentAddress = 0L; private static final OnAddress startOn(long address) { OnAddressImpl instance = new OnAddressImpl(); instance.startAddress = address; instance.currentAddress = address; return instance; } @Override public final OnAddressFollowBy put(int value){ UNSAFE.putInt(currentAddress,value); currentAddress += 4; return this; } @Override public final OnAddressFollowBy put(long value){ UNSAFE.putLong(currentAddress, value); currentAddress += 8; return this; } @Override public final OnAddressFollowBy put(byte value){ UNSAFE.putByte(currentAddress,value); currentAddress += 1; return this; } @Override public final OnAddressFollowBy put(short value){ UNSAFE.putShort(currentAddress, value); currentAddress += 2; return this; } @Override public final OnAddressFollowBy put(float value){ UNSAFE.putFloat(currentAddress, value); currentAddress += 4; return this; } @Override public final OnAddressFollowBy put(double value){ UNSAFE.putDouble(currentAddress, value); currentAddress += 8; return this; } @Override public final OnAddressFollowBy putAddress(long address){ UNSAFE.putAddress(currentAddress,address); currentAddress += SIZE_ADDRESS; return this; } @Override public OnAddressFollowBy self() { return this; } @Override public final OnAddressFollowBy followBy(int value){ put(value); return this; } @Override public OnAddressFollowBy followBy(long value) { put(value); return this; } @Override public OnAddressFollowBy followBy(byte value) { put(value); return this; } @Override public OnAddressFollowBy followBy(short value) { put(value); return this; } @Override public OnAddressFollowBy followBy(float value) { put(value); return this; } @Override public OnAddressFollowBy followBy(double value) { put(value); return this; } @Override public OnAddressFollowBy followByAddress(long address) { putAddress(address); return this; } @Override public OnAddressFollowBy paddedBy(long length, byte paddingValue) { UNSAFE.setMemory(currentAddress, length, paddingValue); currentAddress += length; return this; } @Override public OnAddressFollowBy paddedBy(long length) { paddedBy(length, (byte) 0); return this; } @Override public OnAddressFollowBy paddedToNextNearestMachineWordAlignedAddress() { long addr = nextNearestMachineWordAlignedAddress(currentAddress); long length = addr-currentAddress; if (length!=0) paddedBy(length); return this; } @Override public OnAddressFollowBy paddedToNextNearestCacheLineAlignedAddress() { long addr = nextNearestCacheLineAlignedAddress(currentAddress); long length = addr-currentAddress; if (length!=0) paddedBy(length); return this; } @Override public OnAddressFollowBy paddedToNextNearestPageAlignedAddress() { long addr = nextNearestPageAlignedAddress(currentAddress); long length = addr-currentAddress; if (length!=0) paddedBy(length); return this; } @Override public OnAddressFollowBy paddedToNextMachineWordAlignedAddress() { paddedBy(nextMachineWordAlignedAddress(currentAddress) -currentAddress); return this; } @Override public OnAddressFollowBy paddedToNextCacheLineAlignedAddress() { paddedBy(nextCacheLineAlignedAddress(currentAddress) -currentAddress); return this; } @Override public OnAddressFollowBy paddedToNextPageAlignedAddress() { paddedBy(nextPageAlignedAddress(currentAddress) -currentAddress); return this; } @Override public OnAddressFollowBy gotoNextNearestMachineWordAlignedAddress() { currentAddress = nextNearestMachineWordAlignedAddress(currentAddress); return this; } @Override public OnAddressFollowBy gotoNextNearestCacheLineAlignedAddress() { currentAddress = nextNearestCacheLineAlignedAddress(currentAddress); return this; } @Override public OnAddressFollowBy gotoNextNearestPageAlignedAddress() { currentAddress = nextNearestPageAlignedAddress(currentAddress); return this; } @Override public OnAddressFollowBy gotoNextMachineWordAlignedAddress() { currentAddress = nextMachineWordAlignedAddress(currentAddress); return this; } @Override public OnAddressFollowBy gotoNextCacheLineAlignedAddress() { currentAddress = nextCacheLineAlignedAddress(currentAddress); return this; } @Override public OnAddressFollowBy gotoNextPageAlignedAddress() { currentAddress = nextPageAlignedAddress(currentAddress); return this; } @Override public OnAddressFollowBy gotoAddress(long address) { currentAddress = address; return this; } @Override public long endAddress() { return currentAddress; } @Override public byte[] toByteArray() { //length>=1 int length = (int)(currentAddress-startAddress); byte[] rt = new byte[length]; long offset = UNSAFE.ARRAY_BYTE_BASE_OFFSET; UNSAFE.copyMemory(null,startAddress,rt,offset,length); return rt; } @Override public void toByteArray(byte[] dst) { //contract(()->dst.length>0); int lengthSrc = (int)(currentAddress-startAddress+1); int lengthDst = dst.length; int minLength = lengthSrc<lengthDst ? lengthDst : dst.length; long offset = UNSAFE.ARRAY_BYTE_BASE_OFFSET; UNSAFE.copyMemory(null,startAddress,dst,offset,minLength); } } @NotThreadSafe public static interface OnAddress { public OnAddressFollowBy put(int value); public OnAddressFollowBy put(long value); public OnAddressFollowBy put(byte value); public OnAddressFollowBy put(short value); public OnAddressFollowBy put(float value); public OnAddressFollowBy put(double value); /** * <p> * NOTE: <p> * 1. currentAddress is unsigned int <p> * 2. currentAddress size depends on the system <p> * 3. use currentAddress as currentAddress for all usages <p> * (Don't mess up with long unless you know what you are doing) <p> */ public OnAddressFollowBy putAddress(long address); /** * return the instance itself as a convenient method to * start padding directly. */ public OnAddressFollowBy self(); } public static interface OnAddressFollowBy { public OnAddressFollowBy followBy(int value); public OnAddressFollowBy followBy(long value); public OnAddressFollowBy followBy(byte value); public OnAddressFollowBy followBy(short value); public OnAddressFollowBy followBy(float value); public OnAddressFollowBy followBy(double value); public OnAddressFollowBy followByAddress(long address); /** * the current address will be padded wtih some paddingValue in some length. * <p> */ public OnAddressFollowBy paddedBy(long length, byte paddingValue); /** * same to call with paddedBy(length, 0) * <p> */ public OnAddressFollowBy paddedBy(long length); public OnAddressFollowBy paddedToNextNearestMachineWordAlignedAddress(); public OnAddressFollowBy paddedToNextNearestCacheLineAlignedAddress(); public OnAddressFollowBy paddedToNextNearestPageAlignedAddress(); public OnAddressFollowBy paddedToNextMachineWordAlignedAddress(); public OnAddressFollowBy paddedToNextCacheLineAlignedAddress(); public OnAddressFollowBy paddedToNextPageAlignedAddress(); public OnAddressFollowBy gotoNextNearestMachineWordAlignedAddress(); public OnAddressFollowBy gotoNextNearestCacheLineAlignedAddress(); public OnAddressFollowBy gotoNextNearestPageAlignedAddress(); public OnAddressFollowBy gotoNextMachineWordAlignedAddress(); public OnAddressFollowBy gotoNextCacheLineAlignedAddress(); public OnAddressFollowBy gotoNextPageAlignedAddress(); public OnAddressFollowBy gotoAddress(long address); public long endAddress(); /** * XXX: we may need an end() to seal the whole DSL? * <p> * @return dst - a new byte array contains the content from starting to * current address. */ public byte[] toByteArray(); /** * variant of {@link #toByteArray()} * contract: dst.length>0 * <p> * the content of dst will be dst.length bytes of the content from starting * to current address. The remaining content of dst after outputting is * untouchded. * */ public void toByteArray(byte[] dst); } public static final OnAddress onAddress(long address) { return OnAddressImpl.startOn(address); } /** * Returns the thread id for the given thread. We must access * this directly rather than via method Thread.getId() because * getId() is not final, and has been known to be overridden in * ways that do not preserve unique mappings. */ private static final long TID_OFFSET = uncheckTo(() -> { return UNSAFE.objectFieldOffset(Thread.class.getDeclaredField("tid")); }); /** * we don't use volatile version, based on the following reasons/assumptions: * 1. init() called by Thread constructor is before start(); * 2. "A call to start() on a thread happens-before any actions in the * started thread"; * 3. there is no tid modifier (in source code form) except Thread itself * (so "effective final"); * * @param thread * @return */ public static final long threadId(Thread thread) { return UNSAFE.getLong(thread, TID_OFFSET); } public static final long currentThreadId() { return UNSAFE.getLong(Thread.currentThread(), TID_OFFSET); } }