Path: blob/master/src/java.base/share/classes/sun/nio/ch/Util.java
41159 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.ch;2627import java.io.FileDescriptor;28import java.io.IOException;29import java.lang.reflect.Constructor;30import java.lang.reflect.InvocationTargetException;31import java.nio.ByteBuffer;32import java.nio.MappedByteBuffer;33import java.security.AccessController;34import java.security.PrivilegedAction;35import java.util.Collection;36import java.util.Iterator;37import java.util.Set;3839import jdk.internal.access.foreign.MemorySegmentProxy;40import jdk.internal.misc.TerminatingThreadLocal;41import jdk.internal.misc.Unsafe;42import sun.security.action.GetPropertyAction;4344public class Util {4546// -- Caches --4748// The number of temp buffers in our pool49private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;5051// The max size allowed for a cached temp buffer, in bytes52private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize();5354// Per-thread cache of temporary direct buffers55private static ThreadLocal<BufferCache> bufferCache = new TerminatingThreadLocal<>() {56@Override57protected BufferCache initialValue() {58return new BufferCache();59}60@Override61protected void threadTerminated(BufferCache cache) { // will never be null62while (!cache.isEmpty()) {63ByteBuffer bb = cache.removeFirst();64free(bb);65}66}67};6869/**70* Returns the max size allowed for a cached temp buffers, in71* bytes. It defaults to Long.MAX_VALUE. It can be set with the72* jdk.nio.maxCachedBufferSize property. Even though73* ByteBuffer.capacity() returns an int, we're using a long here74* for potential future-proofing.75*/76private static long getMaxCachedBufferSize() {77String s = GetPropertyAction78.privilegedGetProperty("jdk.nio.maxCachedBufferSize");79if (s != null) {80try {81long m = Long.parseLong(s);82if (m >= 0) {83return m;84} else {85// if it's negative, ignore the system property86}87} catch (NumberFormatException e) {88// if the string is not well formed, ignore the system property89}90}91return Long.MAX_VALUE;92}9394/**95* Returns true if a buffer of this size is too large to be96* added to the buffer cache, false otherwise.97*/98private static boolean isBufferTooLarge(int size) {99return size > MAX_CACHED_BUFFER_SIZE;100}101102/**103* Returns true if the buffer is too large to be added to the104* buffer cache, false otherwise.105*/106private static boolean isBufferTooLarge(ByteBuffer buf) {107return isBufferTooLarge(buf.capacity());108}109110/**111* A simple cache of direct buffers.112*/113private static class BufferCache {114// the array of buffers115private ByteBuffer[] buffers;116117// the number of buffers in the cache118private int count;119120// the index of the first valid buffer (undefined if count == 0)121private int start;122123private int next(int i) {124return (i + 1) % TEMP_BUF_POOL_SIZE;125}126127BufferCache() {128buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];129}130131/**132* Removes and returns a buffer from the cache of at least the given133* size (or null if no suitable buffer is found).134*/135ByteBuffer get(int size) {136// Don't call this if the buffer would be too large.137assert !isBufferTooLarge(size);138139if (count == 0)140return null; // cache is empty141142ByteBuffer[] buffers = this.buffers;143144// search for suitable buffer (often the first buffer will do)145ByteBuffer buf = buffers[start];146if (buf.capacity() < size) {147buf = null;148int i = start;149while ((i = next(i)) != start) {150ByteBuffer bb = buffers[i];151if (bb == null)152break;153if (bb.capacity() >= size) {154buf = bb;155break;156}157}158if (buf == null)159return null;160// move first element to here to avoid re-packing161buffers[i] = buffers[start];162}163164// remove first element165buffers[start] = null;166start = next(start);167count--;168169// prepare the buffer and return it170buf.rewind();171buf.limit(size);172return buf;173}174175boolean offerFirst(ByteBuffer buf) {176// Don't call this if the buffer is too large.177assert !isBufferTooLarge(buf);178179if (count >= TEMP_BUF_POOL_SIZE) {180return false;181} else {182start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;183buffers[start] = buf;184count++;185return true;186}187}188189boolean offerLast(ByteBuffer buf) {190// Don't call this if the buffer is too large.191assert !isBufferTooLarge(buf);192193if (count >= TEMP_BUF_POOL_SIZE) {194return false;195} else {196int next = (start + count) % TEMP_BUF_POOL_SIZE;197buffers[next] = buf;198count++;199return true;200}201}202203boolean isEmpty() {204return count == 0;205}206207ByteBuffer removeFirst() {208assert count > 0;209ByteBuffer buf = buffers[start];210buffers[start] = null;211start = next(start);212count--;213return buf;214}215}216217/**218* Returns a temporary buffer of at least the given size219*/220public static ByteBuffer getTemporaryDirectBuffer(int size) {221// If a buffer of this size is too large for the cache, there222// should not be a buffer in the cache that is at least as223// large. So we'll just create a new one. Also, we don't have224// to remove the buffer from the cache (as this method does225// below) given that we won't put the new buffer in the cache.226if (isBufferTooLarge(size)) {227return ByteBuffer.allocateDirect(size);228}229230BufferCache cache = bufferCache.get();231ByteBuffer buf = cache.get(size);232if (buf != null) {233return buf;234} else {235// No suitable buffer in the cache so we need to allocate a new236// one. To avoid the cache growing then we remove the first237// buffer from the cache and free it.238if (!cache.isEmpty()) {239buf = cache.removeFirst();240free(buf);241}242return ByteBuffer.allocateDirect(size);243}244}245246/**247* Returns a temporary buffer of at least the given size and248* aligned to the alignment249*/250public static ByteBuffer getTemporaryAlignedDirectBuffer(int size,251int alignment) {252if (isBufferTooLarge(size)) {253return ByteBuffer.allocateDirect(size + alignment - 1)254.alignedSlice(alignment);255}256257BufferCache cache = bufferCache.get();258ByteBuffer buf = cache.get(size);259if (buf != null) {260if (buf.alignmentOffset(0, alignment) == 0) {261return buf;262}263} else {264if (!cache.isEmpty()) {265buf = cache.removeFirst();266free(buf);267}268}269return ByteBuffer.allocateDirect(size + alignment - 1)270.alignedSlice(alignment);271}272273/**274* Releases a temporary buffer by returning to the cache or freeing it.275*/276public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {277offerFirstTemporaryDirectBuffer(buf);278}279280/**281* Releases a temporary buffer by returning to the cache or freeing it. If282* returning to the cache then insert it at the start so that it is283* likely to be returned by a subsequent call to getTemporaryDirectBuffer.284*/285static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {286// If the buffer is too large for the cache we don't have to287// check the cache. We'll just free it.288if (isBufferTooLarge(buf)) {289free(buf);290return;291}292293assert buf != null;294BufferCache cache = bufferCache.get();295if (!cache.offerFirst(buf)) {296// cache is full297free(buf);298}299}300301/**302* Releases a temporary buffer by returning to the cache or freeing it. If303* returning to the cache then insert it at the end. This makes it304* suitable for scatter/gather operations where the buffers are returned to305* cache in same order that they were obtained.306*/307static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {308// If the buffer is too large for the cache we don't have to309// check the cache. We'll just free it.310if (isBufferTooLarge(buf)) {311free(buf);312return;313}314315assert buf != null;316BufferCache cache = bufferCache.get();317if (!cache.offerLast(buf)) {318// cache is full319free(buf);320}321}322323/**324* Frees the memory for the given direct buffer325*/326private static void free(ByteBuffer buf) {327((DirectBuffer)buf).cleaner().clean();328}329330331// -- Random stuff --332333static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {334if ((offset == 0) && (length == bs.length))335return bs;336int n = length;337ByteBuffer[] bs2 = new ByteBuffer[n];338for (int i = 0; i < n; i++)339bs2[i] = bs[offset + i];340return bs2;341}342343static <E> Set<E> ungrowableSet(final Set<E> s) {344return new Set<E>() {345346public int size() { return s.size(); }347public boolean isEmpty() { return s.isEmpty(); }348public boolean contains(Object o) { return s.contains(o); }349public Object[] toArray() { return s.toArray(); }350public <T> T[] toArray(T[] a) { return s.toArray(a); }351public String toString() { return s.toString(); }352public Iterator<E> iterator() { return s.iterator(); }353public boolean equals(Object o) { return s.equals(o); }354public int hashCode() { return s.hashCode(); }355public void clear() { s.clear(); }356public boolean remove(Object o) { return s.remove(o); }357358public boolean containsAll(Collection<?> coll) {359return s.containsAll(coll);360}361public boolean removeAll(Collection<?> coll) {362return s.removeAll(coll);363}364public boolean retainAll(Collection<?> coll) {365return s.retainAll(coll);366}367368public boolean add(E o){369throw new UnsupportedOperationException();370}371public boolean addAll(Collection<? extends E> coll) {372throw new UnsupportedOperationException();373}374375};376}377378379// -- Unsafe access --380381private static Unsafe unsafe = Unsafe.getUnsafe();382383private static byte _get(long a) {384return unsafe.getByte(a);385}386387private static void _put(long a, byte b) {388unsafe.putByte(a, b);389}390391static void erase(ByteBuffer bb) {392unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0);393}394395static Unsafe unsafe() {396return unsafe;397}398399private static int pageSize = -1;400401static int pageSize() {402if (pageSize == -1)403pageSize = unsafe().pageSize();404return pageSize;405}406407private static volatile Constructor<?> directByteBufferConstructor;408409@SuppressWarnings("removal")410private static void initDBBConstructor() {411AccessController.doPrivileged(new PrivilegedAction<Void>() {412public Void run() {413try {414Class<?> cl = Class.forName("java.nio.DirectByteBuffer");415Constructor<?> ctor = cl.getDeclaredConstructor(416new Class<?>[] { int.class,417long.class,418FileDescriptor.class,419Runnable.class,420boolean.class, MemorySegmentProxy.class});421ctor.setAccessible(true);422directByteBufferConstructor = ctor;423} catch (ClassNotFoundException |424NoSuchMethodException |425IllegalArgumentException |426ClassCastException x) {427throw new InternalError(x);428}429return null;430}});431}432433static MappedByteBuffer newMappedByteBuffer(int size, long addr,434FileDescriptor fd,435Runnable unmapper,436boolean isSync)437{438MappedByteBuffer dbb;439if (directByteBufferConstructor == null)440initDBBConstructor();441try {442dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(443new Object[] { size,444addr,445fd,446unmapper,447isSync, null});448} catch (InstantiationException |449IllegalAccessException |450InvocationTargetException e) {451throw new InternalError(e);452}453return dbb;454}455456private static volatile Constructor<?> directByteBufferRConstructor;457458@SuppressWarnings("removal")459private static void initDBBRConstructor() {460AccessController.doPrivileged(new PrivilegedAction<Void>() {461public Void run() {462try {463Class<?> cl = Class.forName("java.nio.DirectByteBufferR");464Constructor<?> ctor = cl.getDeclaredConstructor(465new Class<?>[] { int.class,466long.class,467FileDescriptor.class,468Runnable.class,469boolean.class, MemorySegmentProxy.class });470ctor.setAccessible(true);471directByteBufferRConstructor = ctor;472} catch (ClassNotFoundException |473NoSuchMethodException |474IllegalArgumentException |475ClassCastException x) {476throw new InternalError(x);477}478return null;479}});480}481482static MappedByteBuffer newMappedByteBufferR(int size, long addr,483FileDescriptor fd,484Runnable unmapper,485boolean isSync)486{487MappedByteBuffer dbb;488if (directByteBufferRConstructor == null)489initDBBRConstructor();490try {491dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance(492new Object[] { size,493addr,494fd,495unmapper,496isSync, null});497} catch (InstantiationException |498IllegalAccessException |499InvocationTargetException e) {500throw new InternalError(e);501}502return dbb;503}504505static void checkBufferPositionAligned(ByteBuffer bb,506int pos, int alignment)507throws IOException508{509if (bb.alignmentOffset(pos, alignment) != 0) {510throw new IOException("Current location of the bytebuffer ("511+ pos + ") is not a multiple of the block size ("512+ alignment + ")");513}514}515516static void checkRemainingBufferSizeAligned(int rem,517int alignment)518throws IOException519{520if (rem % alignment != 0) {521throw new IOException("Number of remaining bytes ("522+ rem + ") is not a multiple of the block size ("523+ alignment + ")");524}525}526527static void checkChannelPositionAligned(long position,528int alignment)529throws IOException530{531if (position % alignment != 0) {532throw new IOException("Channel position (" + position533+ ") is not a multiple of the block size ("534+ alignment + ")");535}536}537}538539540