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