|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.nio.ch; |
|
|
|
import java.lang.ref.SoftReference; |
|
import java.lang.reflect.*; |
|
import java.io.IOException; |
|
import java.io.FileDescriptor; |
|
import java.nio.ByteBuffer; |
|
import java.nio.MappedByteBuffer; |
|
import java.nio.channels.*; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.util.*; |
|
import sun.misc.Unsafe; |
|
import sun.misc.Cleaner; |
|
import sun.security.action.GetPropertyAction; |
|
|
|
|
|
public class Util { |
|
|
|
// -- Caches -- |
|
|
|
|
|
private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX; |
|
|
|
|
|
private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize(); |
|
|
|
|
|
private static ThreadLocal<BufferCache> bufferCache = |
|
new ThreadLocal<BufferCache>() |
|
{ |
|
@Override |
|
protected BufferCache initialValue() { |
|
return new BufferCache(); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static long getMaxCachedBufferSize() { |
|
String s = java.security.AccessController.doPrivileged( |
|
new PrivilegedAction<String>() { |
|
@Override |
|
public String run() { |
|
return System.getProperty("jdk.nio.maxCachedBufferSize"); |
|
} |
|
}); |
|
if (s != null) { |
|
try { |
|
long m = Long.parseLong(s); |
|
if (m >= 0) { |
|
return m; |
|
} else { |
|
// if it's negative, ignore the system property |
|
} |
|
} catch (NumberFormatException e) { |
|
// if the string is not well formed, ignore the system property |
|
} |
|
} |
|
return Long.MAX_VALUE; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static boolean isBufferTooLarge(int size) { |
|
return size > MAX_CACHED_BUFFER_SIZE; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static boolean isBufferTooLarge(ByteBuffer buf) { |
|
return isBufferTooLarge(buf.capacity()); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static class BufferCache { |
|
|
|
private ByteBuffer[] buffers; |
|
|
|
|
|
private int count; |
|
|
|
|
|
private int start; |
|
|
|
private int next(int i) { |
|
return (i + 1) % TEMP_BUF_POOL_SIZE; |
|
} |
|
|
|
BufferCache() { |
|
buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE]; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
ByteBuffer get(int size) { |
|
|
|
assert !isBufferTooLarge(size); |
|
|
|
if (count == 0) |
|
return null; |
|
|
|
ByteBuffer[] buffers = this.buffers; |
|
|
|
|
|
ByteBuffer buf = buffers[start]; |
|
if (buf.capacity() < size) { |
|
buf = null; |
|
int i = start; |
|
while ((i = next(i)) != start) { |
|
ByteBuffer bb = buffers[i]; |
|
if (bb == null) |
|
break; |
|
if (bb.capacity() >= size) { |
|
buf = bb; |
|
break; |
|
} |
|
} |
|
if (buf == null) |
|
return null; |
|
|
|
buffers[i] = buffers[start]; |
|
} |
|
|
|
|
|
buffers[start] = null; |
|
start = next(start); |
|
count--; |
|
|
|
|
|
buf.rewind(); |
|
buf.limit(size); |
|
return buf; |
|
} |
|
|
|
boolean offerFirst(ByteBuffer buf) { |
|
|
|
assert !isBufferTooLarge(buf); |
|
|
|
if (count >= TEMP_BUF_POOL_SIZE) { |
|
return false; |
|
} else { |
|
start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE; |
|
buffers[start] = buf; |
|
count++; |
|
return true; |
|
} |
|
} |
|
|
|
boolean offerLast(ByteBuffer buf) { |
|
|
|
assert !isBufferTooLarge(buf); |
|
|
|
if (count >= TEMP_BUF_POOL_SIZE) { |
|
return false; |
|
} else { |
|
int next = (start + count) % TEMP_BUF_POOL_SIZE; |
|
buffers[next] = buf; |
|
count++; |
|
return true; |
|
} |
|
} |
|
|
|
boolean isEmpty() { |
|
return count == 0; |
|
} |
|
|
|
ByteBuffer removeFirst() { |
|
assert count > 0; |
|
ByteBuffer buf = buffers[start]; |
|
buffers[start] = null; |
|
start = next(start); |
|
count--; |
|
return buf; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static ByteBuffer getTemporaryDirectBuffer(int size) { |
|
// If a buffer of this size is too large for the cache, there |
|
// should not be a buffer in the cache that is at least as |
|
// large. So we'll just create a new one. Also, we don't have |
|
// to remove the buffer from the cache (as this method does |
|
|
|
if (isBufferTooLarge(size)) { |
|
return ByteBuffer.allocateDirect(size); |
|
} |
|
|
|
BufferCache cache = bufferCache.get(); |
|
ByteBuffer buf = cache.get(size); |
|
if (buf != null) { |
|
return buf; |
|
} else { |
|
// No suitable buffer in the cache so we need to allocate a new |
|
// one. To avoid the cache growing then we remove the first |
|
|
|
if (!cache.isEmpty()) { |
|
buf = cache.removeFirst(); |
|
free(buf); |
|
} |
|
return ByteBuffer.allocateDirect(size); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static void releaseTemporaryDirectBuffer(ByteBuffer buf) { |
|
offerFirstTemporaryDirectBuffer(buf); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) { |
|
// If the buffer is too large for the cache we don't have to |
|
|
|
if (isBufferTooLarge(buf)) { |
|
free(buf); |
|
return; |
|
} |
|
|
|
assert buf != null; |
|
BufferCache cache = bufferCache.get(); |
|
if (!cache.offerFirst(buf)) { |
|
|
|
free(buf); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static void offerLastTemporaryDirectBuffer(ByteBuffer buf) { |
|
// If the buffer is too large for the cache we don't have to |
|
|
|
if (isBufferTooLarge(buf)) { |
|
free(buf); |
|
return; |
|
} |
|
|
|
assert buf != null; |
|
BufferCache cache = bufferCache.get(); |
|
if (!cache.offerLast(buf)) { |
|
|
|
free(buf); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static void free(ByteBuffer buf) { |
|
((DirectBuffer)buf).cleaner().clean(); |
|
} |
|
|
|
|
|
// -- Random stuff -- |
|
|
|
static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) { |
|
if ((offset == 0) && (length == bs.length)) |
|
return bs; |
|
int n = length; |
|
ByteBuffer[] bs2 = new ByteBuffer[n]; |
|
for (int i = 0; i < n; i++) |
|
bs2[i] = bs[offset + i]; |
|
return bs2; |
|
} |
|
|
|
static <E> Set<E> ungrowableSet(final Set<E> s) { |
|
return new Set<E>() { |
|
|
|
public int size() { return s.size(); } |
|
public boolean isEmpty() { return s.isEmpty(); } |
|
public boolean contains(Object o) { return s.contains(o); } |
|
public Object[] toArray() { return s.toArray(); } |
|
public <T> T[] toArray(T[] a) { return s.toArray(a); } |
|
public String toString() { return s.toString(); } |
|
public Iterator<E> iterator() { return s.iterator(); } |
|
public boolean equals(Object o) { return s.equals(o); } |
|
public int hashCode() { return s.hashCode(); } |
|
public void clear() { s.clear(); } |
|
public boolean remove(Object o) { return s.remove(o); } |
|
|
|
public boolean containsAll(Collection<?> coll) { |
|
return s.containsAll(coll); |
|
} |
|
public boolean removeAll(Collection<?> coll) { |
|
return s.removeAll(coll); |
|
} |
|
public boolean retainAll(Collection<?> coll) { |
|
return s.retainAll(coll); |
|
} |
|
|
|
public boolean add(E o){ |
|
throw new UnsupportedOperationException(); |
|
} |
|
public boolean addAll(Collection<? extends E> coll) { |
|
throw new UnsupportedOperationException(); |
|
} |
|
|
|
}; |
|
} |
|
|
|
|
|
// -- Unsafe access -- |
|
|
|
private static Unsafe unsafe = Unsafe.getUnsafe(); |
|
|
|
private static byte _get(long a) { |
|
return unsafe.getByte(a); |
|
} |
|
|
|
private static void _put(long a, byte b) { |
|
unsafe.putByte(a, b); |
|
} |
|
|
|
static void erase(ByteBuffer bb) { |
|
unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0); |
|
} |
|
|
|
static Unsafe unsafe() { |
|
return unsafe; |
|
} |
|
|
|
private static int pageSize = -1; |
|
|
|
static int pageSize() { |
|
if (pageSize == -1) |
|
pageSize = unsafe().pageSize(); |
|
return pageSize; |
|
} |
|
|
|
private static volatile Constructor<?> directByteBufferConstructor = null; |
|
|
|
private static void initDBBConstructor() { |
|
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
public Void run() { |
|
try { |
|
Class<?> cl = Class.forName("java.nio.DirectByteBuffer"); |
|
Constructor<?> ctor = cl.getDeclaredConstructor( |
|
new Class<?>[] { int.class, |
|
long.class, |
|
FileDescriptor.class, |
|
Runnable.class }); |
|
ctor.setAccessible(true); |
|
directByteBufferConstructor = ctor; |
|
} catch (ClassNotFoundException | |
|
NoSuchMethodException | |
|
IllegalArgumentException | |
|
ClassCastException x) { |
|
throw new InternalError(x); |
|
} |
|
return null; |
|
}}); |
|
} |
|
|
|
static MappedByteBuffer newMappedByteBuffer(int size, long addr, |
|
FileDescriptor fd, |
|
Runnable unmapper) |
|
{ |
|
MappedByteBuffer dbb; |
|
if (directByteBufferConstructor == null) |
|
initDBBConstructor(); |
|
try { |
|
dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance( |
|
new Object[] { new Integer(size), |
|
new Long(addr), |
|
fd, |
|
unmapper }); |
|
} catch (InstantiationException | |
|
IllegalAccessException | |
|
InvocationTargetException e) { |
|
throw new InternalError(e); |
|
} |
|
return dbb; |
|
} |
|
|
|
private static volatile Constructor<?> directByteBufferRConstructor = null; |
|
|
|
private static void initDBBRConstructor() { |
|
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
public Void run() { |
|
try { |
|
Class<?> cl = Class.forName("java.nio.DirectByteBufferR"); |
|
Constructor<?> ctor = cl.getDeclaredConstructor( |
|
new Class<?>[] { int.class, |
|
long.class, |
|
FileDescriptor.class, |
|
Runnable.class }); |
|
ctor.setAccessible(true); |
|
directByteBufferRConstructor = ctor; |
|
} catch (ClassNotFoundException | |
|
NoSuchMethodException | |
|
IllegalArgumentException | |
|
ClassCastException x) { |
|
throw new InternalError(x); |
|
} |
|
return null; |
|
}}); |
|
} |
|
|
|
static MappedByteBuffer newMappedByteBufferR(int size, long addr, |
|
FileDescriptor fd, |
|
Runnable unmapper) |
|
{ |
|
MappedByteBuffer dbb; |
|
if (directByteBufferRConstructor == null) |
|
initDBBRConstructor(); |
|
try { |
|
dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance( |
|
new Object[] { new Integer(size), |
|
new Long(addr), |
|
fd, |
|
unmapper }); |
|
} catch (InstantiationException | |
|
IllegalAccessException | |
|
InvocationTargetException e) { |
|
throw new InternalError(e); |
|
} |
|
return dbb; |
|
} |
|
|
|
|
|
// -- Bug compatibility -- |
|
|
|
private static volatile String bugLevel = null; |
|
|
|
static boolean atBugLevel(String bl) { |
|
if (bugLevel == null) { |
|
if (!sun.misc.VM.isBooted()) |
|
return false; |
|
String value = AccessController.doPrivileged( |
|
new GetPropertyAction("sun.nio.ch.bugLevel")); |
|
bugLevel = (value != null) ? value : ""; |
|
} |
|
return bugLevel.equals(bl); |
|
} |
|
|
|
} |