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