1 /*
  2  * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package sun.nio.ch;
 27 
 28 import java.io.FileDescriptor;
 29 import java.io.IOException;
 30 import java.lang.reflect.Constructor;
 31 import java.lang.reflect.InvocationTargetException;
 32 import java.nio.ByteBuffer;
 33 import java.nio.MappedByteBuffer;
 34 import java.security.AccessController;
 35 import java.security.PrivilegedAction;
 36 import java.util.Collection;
 37 import java.util.Iterator;
 38 import java.util.Set;
 39 
 40 import jdk.internal.access.JavaLangAccess;
 41 import jdk.internal.access.SharedSecrets;
 42 import jdk.internal.access.foreign.MemorySegmentProxy;
 43 import jdk.internal.misc.TerminatingThreadLocal;
 44 import jdk.internal.misc.Unsafe;
 45 import sun.security.action.GetPropertyAction;
 46 
 47 public class Util {
 48     private static JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
 49 
 50     // -- Caches --
 51 
 52     // The number of temp buffers in our pool
 53     private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX;
 54 
 55     // The max size allowed for a cached temp buffer, in bytes
 56     private static final long MAX_CACHED_BUFFER_SIZE = getMaxCachedBufferSize();
 57 
 58     // Per-thread cache of temporary direct buffers
 59     private static ThreadLocal<BufferCache> bufferCache = new TerminatingThreadLocal<>() {
 60         @Override
 61         protected BufferCache initialValue() {
 62             return new BufferCache();
 63         }
 64         @Override
 65         protected void threadTerminated(BufferCache cache) { // will never be null
 66             while (!cache.isEmpty()) {
 67                 ByteBuffer bb = cache.removeFirst();
 68                 free(bb);
 69             }
 70         }
 71     };
 72 
 73     /**
 74      * Returns the max size allowed for a cached temp buffers, in
 75      * bytes. It defaults to Long.MAX_VALUE. It can be set with the
 76      * jdk.nio.maxCachedBufferSize property. Even though
 77      * ByteBuffer.capacity() returns an int, we're using a long here
 78      * for potential future-proofing.
 79      */
 80     private static long getMaxCachedBufferSize() {
 81         String s = GetPropertyAction.privilegedGetProperty("jdk.nio.maxCachedBufferSize");
 82         if (s != null) {
 83             try {
 84                 long m = Long.parseLong(s);
 85                 if (m >= 0) {
 86                     return m;
 87                 } else {
 88                     // if it's negative, ignore the system property
 89                 }
 90             } catch (NumberFormatException e) {
 91                 // if the string is not well formed, ignore the system property
 92             }
 93         }
 94         return Long.MAX_VALUE;
 95     }
 96 
 97     /**
 98      * Returns true if a buffer of this size is too large to be
 99      * added to the buffer cache, false otherwise.
100      */
101     private static boolean isBufferTooLarge(int size) {
102         return size > MAX_CACHED_BUFFER_SIZE;
103     }
104 
105     /**
106      * Returns true if the buffer is too large to be added to the
107      * buffer cache, false otherwise.
108      */
109     private static boolean isBufferTooLarge(ByteBuffer buf) {
110         return isBufferTooLarge(buf.capacity());
111     }
112 
113     /**
114      * A simple cache of direct buffers.
115      */
116     private static class BufferCache {
117         // the array of buffers
118         private ByteBuffer[] buffers;
119 
120         // the number of buffers in the cache
121         private int count;
122 
123         // the index of the first valid buffer (undefined if count == 0)
124         private int start;
125 
126         private int next(int i) {
127             return (i + 1) % TEMP_BUF_POOL_SIZE;
128         }
129 
130         BufferCache() {
131             buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE];
132         }
133 
134         /**
135          * Removes and returns a buffer from the cache of at least the given
136          * size (or null if no suitable buffer is found).
137          */
138         ByteBuffer get(int size) {
139             // Don't call this if the buffer would be too large.
140             assert !isBufferTooLarge(size);
141 
142             if (count == 0)
143                 return null;  // cache is empty
144 
145             ByteBuffer[] buffers = this.buffers;
146 
147             // search for suitable buffer (often the first buffer will do)
148             ByteBuffer buf = buffers[start];
149             if (buf.capacity() < size) {
150                 buf = null;
151                 int i = start;
152                 while ((i = next(i)) != start) {
153                     ByteBuffer bb = buffers[i];
154                     if (bb == null)
155                         break;
156                     if (bb.capacity() >= size) {
157                         buf = bb;
158                         break;
159                     }
160                 }
161                 if (buf == null)
162                     return null;
163                 // move first element to here to avoid re-packing
164                 buffers[i] = buffers[start];
165             }
166 
167             // remove first element
168             buffers[start] = null;
169             start = next(start);
170             count--;
171 
172             // prepare the buffer and return it
173             buf.rewind();
174             buf.limit(size);
175             return buf;
176         }
177 
178         boolean offerFirst(ByteBuffer buf) {
179             // Don't call this if the buffer is too large.
180             assert !isBufferTooLarge(buf);
181 
182             if (count >= TEMP_BUF_POOL_SIZE) {
183                 return false;
184             } else {
185                 start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE;
186                 buffers[start] = buf;
187                 count++;
188                 return true;
189             }
190         }
191 
192         boolean offerLast(ByteBuffer buf) {
193             // Don't call this if the buffer is too large.
194             assert !isBufferTooLarge(buf);
195 
196             if (count >= TEMP_BUF_POOL_SIZE) {
197                 return false;
198             } else {
199                 int next = (start + count) % TEMP_BUF_POOL_SIZE;
200                 buffers[next] = buf;
201                 count++;
202                 return true;
203             }
204         }
205 
206         boolean isEmpty() {
207             return count == 0;
208         }
209 
210         ByteBuffer removeFirst() {
211             assert count > 0;
212             ByteBuffer buf = buffers[start];
213             buffers[start] = null;
214             start = next(start);
215             count--;
216             return buf;
217         }
218     }
219 
220     /**
221      * Returns a temporary buffer of at least the given size
222      */
223     public static ByteBuffer getTemporaryDirectBuffer(int size) {
224         // If a buffer of this size is too large for the cache, there
225         // should not be a buffer in the cache that is at least as
226         // large. So we'll just create a new one. Also, we don't have
227         // to remove the buffer from the cache (as this method does
228         // below) given that we won't put the new buffer in the cache.
229         if (isBufferTooLarge(size)) {
230             return ByteBuffer.allocateDirect(size);
231         }
232 
233         BufferCache cache = JLA.getCarrierThreadLocal(bufferCache);
234         ByteBuffer buf = cache.get(size);
235         if (buf != null) {
236             return buf;
237         } else {
238             // No suitable buffer in the cache so we need to allocate a new
239             // one. To avoid the cache growing then we remove the first
240             // buffer from the cache and free it.
241             if (!cache.isEmpty()) {
242                 buf = cache.removeFirst();
243                 free(buf);
244             }
245             return ByteBuffer.allocateDirect(size);
246         }
247     }
248 
249     /**
250      * Returns a temporary buffer of at least the given size and
251      * aligned to the alignment
252      */
253     public static ByteBuffer getTemporaryAlignedDirectBuffer(int size,
254                                                              int alignment) {
255         if (isBufferTooLarge(size)) {
256             return ByteBuffer.allocateDirect(size + alignment - 1)
257                     .alignedSlice(alignment);
258         }
259 
260         BufferCache cache = JLA.getCarrierThreadLocal(bufferCache);
261         ByteBuffer buf = cache.get(size);
262         if (buf != null) {
263             if (buf.alignmentOffset(0, alignment) == 0) {
264                 return buf;
265             }
266         } else {
267             if (!cache.isEmpty()) {
268                 buf = cache.removeFirst();
269                 free(buf);
270             }
271         }
272         return ByteBuffer.allocateDirect(size + alignment - 1)
273                 .alignedSlice(alignment);
274     }
275 
276     /**
277      * Releases a temporary buffer by returning to the cache or freeing it.
278      */
279     public static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
280         offerFirstTemporaryDirectBuffer(buf);
281     }
282 
283     /**
284      * Releases a temporary buffer by returning to the cache or freeing it. If
285      * returning to the cache then insert it at the start so that it is
286      * likely to be returned by a subsequent call to getTemporaryDirectBuffer.
287      */
288     static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) {
289         // If the buffer is too large for the cache we don't have to
290         // check the cache. We'll just free it.
291         if (isBufferTooLarge(buf)) {
292             free(buf);
293             return;
294         }
295 
296         assert buf != null;
297         BufferCache cache = JLA.getCarrierThreadLocal(bufferCache);
298         if (!cache.offerFirst(buf)) {
299             // cache is full
300             free(buf);
301         }
302     }
303 
304     /**
305      * Releases a temporary buffer by returning to the cache or freeing it. If
306      * returning to the cache then insert it at the end. This makes it
307      * suitable for scatter/gather operations where the buffers are returned to
308      * cache in same order that they were obtained.
309      */
310     static void offerLastTemporaryDirectBuffer(ByteBuffer buf) {
311         // If the buffer is too large for the cache we don't have to
312         // check the cache. We'll just free it.
313         if (isBufferTooLarge(buf)) {
314             free(buf);
315             return;
316         }
317 
318         assert buf != null;
319         BufferCache cache = JLA.getCarrierThreadLocal(bufferCache);
320         if (!cache.offerLast(buf)) {
321             // cache is full
322             free(buf);
323         }
324     }
325 
326     /**
327      * Frees the memory for the given direct buffer
328      */
329     private static void free(ByteBuffer buf) {
330         ((DirectBuffer)buf).cleaner().clean();
331     }
332 
333 
334     // -- Random stuff --
335 
336     static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) {
337         if ((offset == 0) && (length == bs.length))
338             return bs;
339         int n = length;
340         ByteBuffer[] bs2 = new ByteBuffer[n];
341         for (int i = 0; i < n; i++)
342             bs2[i] = bs[offset + i];
343         return bs2;
344     }
345 
346     static <E> Set<E> ungrowableSet(final Set<E> s) {
347         return new Set<E>() {
348 
349                 public int size()                 { return s.size(); }
350                 public boolean isEmpty()          { return s.isEmpty(); }
351                 public boolean contains(Object o) { return s.contains(o); }
352                 public Object[] toArray()         { return s.toArray(); }
353                 public <T> T[] toArray(T[] a)     { return s.toArray(a); }
354                 public String toString()          { return s.toString(); }
355                 public Iterator<E> iterator()     { return s.iterator(); }
356                 public boolean equals(Object o)   { return s.equals(o); }
357                 public int hashCode()             { return s.hashCode(); }
358                 public void clear()               { s.clear(); }
359                 public boolean remove(Object o)   { return s.remove(o); }
360 
361                 public boolean containsAll(Collection<?> coll) {
362                     return s.containsAll(coll);
363                 }
364                 public boolean removeAll(Collection<?> coll) {
365                     return s.removeAll(coll);
366                 }
367                 public boolean retainAll(Collection<?> coll) {
368                     return s.retainAll(coll);
369                 }
370 
371                 public boolean add(E o){
372                     throw new UnsupportedOperationException();
373                 }
374                 public boolean addAll(Collection<? extends E> coll) {
375                     throw new UnsupportedOperationException();
376                 }
377 
378         };
379     }
380 
381 
382     // -- Unsafe access --
383 
384     private static Unsafe unsafe = Unsafe.getUnsafe();
385 
386     private static byte _get(long a) {
387         return unsafe.getByte(a);
388     }
389 
390     private static void _put(long a, byte b) {
391         unsafe.putByte(a, b);
392     }
393 
394     static void erase(ByteBuffer bb) {
395         unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0);
396     }
397 
398     static Unsafe unsafe() {
399         return unsafe;
400     }
401 
402     private static int pageSize = -1;
403 
404     static int pageSize() {
405         if (pageSize == -1)
406             pageSize = unsafe().pageSize();
407         return pageSize;
408     }
409 
410     private static volatile Constructor<?> directByteBufferConstructor;
411 
412     @SuppressWarnings("removal")
413     private static void initDBBConstructor() {
414         AccessController.doPrivileged(new PrivilegedAction<Void>() {
415                 public Void run() {
416                     try {
417                         Class<?> cl = Class.forName("java.nio.DirectByteBuffer");
418                         Constructor<?> ctor = cl.getDeclaredConstructor(
419                             new Class<?>[] { int.class,
420                                              long.class,
421                                              FileDescriptor.class,
422                                              Runnable.class,
423                                              boolean.class, MemorySegmentProxy.class});
424                         ctor.setAccessible(true);
425                         directByteBufferConstructor = ctor;
426                     } catch (ClassNotFoundException   |
427                              NoSuchMethodException    |
428                              IllegalArgumentException |
429                              ClassCastException x) {
430                         throw new InternalError(x);
431                     }
432                     return null;
433                 }});
434     }
435 
436     static MappedByteBuffer newMappedByteBuffer(int size, long addr,
437                                                 FileDescriptor fd,
438                                                 Runnable unmapper,
439                                                 boolean isSync)
440     {
441         MappedByteBuffer dbb;
442         if (directByteBufferConstructor == null)
443             initDBBConstructor();
444         try {
445             dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(
446               new Object[] { size,
447                              addr,
448                              fd,
449                              unmapper,
450                              isSync, null});
451         } catch (InstantiationException |
452                  IllegalAccessException |
453                  InvocationTargetException e) {
454             throw new InternalError(e);
455         }
456         return dbb;
457     }
458 
459     private static volatile Constructor<?> directByteBufferRConstructor;
460 
461     @SuppressWarnings("removal")
462     private static void initDBBRConstructor() {
463         AccessController.doPrivileged(new PrivilegedAction<Void>() {
464                 public Void run() {
465                     try {
466                         Class<?> cl = Class.forName("java.nio.DirectByteBufferR");
467                         Constructor<?> ctor = cl.getDeclaredConstructor(
468                             new Class<?>[] { int.class,
469                                              long.class,
470                                              FileDescriptor.class,
471                                              Runnable.class,
472                                              boolean.class, MemorySegmentProxy.class });
473                         ctor.setAccessible(true);
474                         directByteBufferRConstructor = ctor;
475                     } catch (ClassNotFoundException |
476                              NoSuchMethodException |
477                              IllegalArgumentException |
478                              ClassCastException x) {
479                         throw new InternalError(x);
480                     }
481                     return null;
482                 }});
483     }
484 
485     static MappedByteBuffer newMappedByteBufferR(int size, long addr,
486                                                  FileDescriptor fd,
487                                                  Runnable unmapper,
488                                                  boolean isSync)
489     {
490         MappedByteBuffer dbb;
491         if (directByteBufferRConstructor == null)
492             initDBBRConstructor();
493         try {
494             dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance(
495               new Object[] { size,
496                              addr,
497                              fd,
498                              unmapper,
499                              isSync, null});
500         } catch (InstantiationException |
501                  IllegalAccessException |
502                  InvocationTargetException e) {
503             throw new InternalError(e);
504         }
505         return dbb;
506     }
507 
508     static void checkBufferPositionAligned(ByteBuffer bb,
509                                                      int pos, int alignment)
510         throws IOException
511     {
512         if (bb.alignmentOffset(pos, alignment) != 0) {
513             throw new IOException("Current location of the bytebuffer ("
514                 + pos + ") is not a multiple of the block size ("
515                 + alignment + ")");
516         }
517     }
518 
519     static void checkRemainingBufferSizeAligned(int rem,
520                                                           int alignment)
521         throws IOException
522     {
523         if (rem % alignment != 0) {
524             throw new IOException("Number of remaining bytes ("
525                 + rem + ") is not a multiple of the block size ("
526                 + alignment + ")");
527         }
528     }
529 
530     static void checkChannelPositionAligned(long position,
531                                                       int alignment)
532         throws IOException
533     {
534         if (position % alignment != 0) {
535            throw new IOException("Channel position (" + position
536                + ") is not a multiple of the block size ("
537                + alignment + ")");
538         }
539     }
540 }