/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.runtime.objects.atomics; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import sun.misc.Unsafe; import sun.nio.ch.DirectBuffer; /** * */ @SuppressWarnings("restriction") final class UnsafeHolder { private UnsafeHolder() { } private static final Unsafe UNSAFE = initializeUnsafe(); private static final long BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); private static Unsafe initializeUnsafe() { try { return Unsafe.getUnsafe(); } catch (SecurityException e) { try { return AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); }); } catch (PrivilegedActionException e2) { throw new ExceptionInInitializerError(e2.getException()); } } } private static Object baseObject(ByteBuffer buffer) { if (buffer.hasArray()) { return buffer.array(); } return null; } private static long offsetOrMemoryAddress(ByteBuffer buffer, int index, int size) { if (index < 0 || size > buffer.limit() - index) { throw new IndexOutOfBoundsException(indexOutOfBoundsMessage(buffer, index, size)); } if (buffer.isReadOnly()) { throw new ReadOnlyBufferException(); } if (buffer.hasArray()) { return BYTE_ARRAY_BASE_OFFSET + buffer.arrayOffset() + index; } assert buffer.isDirect(); return ((DirectBuffer) buffer).address() + index; } private static String indexOutOfBoundsMessage(ByteBuffer buffer, int index, int size) { return String.format("limit=%d, index=%d, size=%d", buffer.limit(), index, size); } /** * Calls {@link Unsafe#fullFence()}. */ static void fullFence() { UNSAFE.fullFence(); } /** * Calls {@link Unsafe#compareAndSwapInt(Object, long, int, int)}. * * @param buffer * the byte buffer * @param index * the byte buffer index * @return the value */ static boolean compareAndSwapInt(ByteBuffer buffer, int index, int expected, int update) { Object base = baseObject(buffer); long offset = offsetOrMemoryAddress(buffer, index, Integer.BYTES); return UNSAFE.compareAndSwapInt(base, offset, expected, update); } /** * Calls {@link Unsafe#getByteVolatile(Object, long)}. * * @param buffer * the byte buffer * @param index * the byte buffer index * @return the value */ static byte getByteVolatile(ByteBuffer buffer, int index) { Object base = baseObject(buffer); long offset = offsetOrMemoryAddress(buffer, index, Byte.BYTES); return UNSAFE.getByteVolatile(base, offset); } /** * Calls {@link Unsafe#getShortVolatile(Object, long)}. * * @param buffer * the byte buffer * @param index * the byte buffer index * @return the value */ static short getShortVolatile(ByteBuffer buffer, int index) { Object base = baseObject(buffer); long offset = offsetOrMemoryAddress(buffer, index, Short.BYTES); return UNSAFE.getShortVolatile(base, offset); } /** * Calls {@link Unsafe#getIntVolatile(Object, long)}. * * @param buffer * the byte buffer * @param index * the byte buffer index * @return the value */ static int getIntVolatile(ByteBuffer buffer, int index) { Object base = baseObject(buffer); long offset = offsetOrMemoryAddress(buffer, index, Integer.BYTES); return UNSAFE.getIntVolatile(base, offset); } /** * Calls {@link Unsafe#putByteVolatile(Object, long, byte)}. * * @param buffer * the byte buffer * @param index * the byte buffer index * @param value * the value */ static void putByteVolatile(ByteBuffer buffer, int index, byte value) { Object base = baseObject(buffer); long offset = offsetOrMemoryAddress(buffer, index, Byte.BYTES); UNSAFE.putByteVolatile(base, offset, value); } /** * Calls {@link Unsafe#putShortVolatile(Object, long, int)}. * * @param buffer * the byte buffer * @param index * the byte buffer index * @param value * the value */ static void putShortVolatile(ByteBuffer buffer, int index, short value) { Object base = baseObject(buffer); long offset = offsetOrMemoryAddress(buffer, index, Short.BYTES); UNSAFE.putShortVolatile(base, offset, value); } /** * Calls {@link Unsafe#putIntVolatile(Object, long, int)}. * * @param buffer * the byte buffer * @param index * the byte buffer index * @param value * the value */ static void putIntVolatile(ByteBuffer buffer, int index, int value) { Object base = baseObject(buffer); long offset = offsetOrMemoryAddress(buffer, index, Integer.BYTES); UNSAFE.putIntVolatile(base, offset, value); } }