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.nio.ByteBuffer;
 31 import java.util.Objects;
 32 import jdk.internal.access.JavaNioAccess;
 33 import jdk.internal.access.SharedSecrets;
 34 import jdk.internal.misc.ScopedMemoryAccess.Scope;
 35 
 36 /**
 37  * File-descriptor based I/O utilities that are shared by NIO classes.
 38  */
 39 
 40 public class IOUtil {
 41 
 42     /**
 43      * Max number of iovec structures that readv/writev supports
 44      */
 45     static final int IOV_MAX;
 46 
 47     /**
 48      * Max total number of bytes that writev supports
 49      */
 50     static final long WRITEV_MAX;
 51 
 52     private IOUtil() { }                // No instantiation
 53 
 54     static int write(FileDescriptor fd, ByteBuffer src, long position,
 55                      NativeDispatcher nd)
 56         throws IOException
 57     {
 58         return write(fd, src, position, false, false, -1, nd);
 59     }
 60 
 61     static int write(FileDescriptor fd, ByteBuffer src, long position,
 62                      boolean async, NativeDispatcher nd)
 63         throws IOException
 64     {
 65         return write(fd, src, position, false, async, -1, nd);
 66     }
 67 
 68     static int write(FileDescriptor fd, ByteBuffer src, long position,
 69                      boolean directIO, int alignment, NativeDispatcher nd)
 70         throws IOException
 71     {
 72         return write(fd, src, position, directIO, false, alignment, nd);
 73     }
 74 
 75     static int write(FileDescriptor fd, ByteBuffer src, long position,
 76                      boolean directIO, boolean async, int alignment,
 77                      NativeDispatcher nd)
 78         throws IOException
 79     {
 80         if (src instanceof DirectBuffer) {
 81             return writeFromNativeBuffer(fd, src, position, directIO, async, alignment, nd);
 82         }
 83 
 84         // Substitute a native buffer
 85         int pos = src.position();
 86         int lim = src.limit();
 87         assert (pos <= lim);
 88         int rem = (pos <= lim ? lim - pos : 0);
 89         ByteBuffer bb;
 90         if (directIO) {
 91             Util.checkRemainingBufferSizeAligned(rem, alignment);
 92             bb = Util.getTemporaryAlignedDirectBuffer(rem, alignment);
 93         } else {
 94             bb = Util.getTemporaryDirectBuffer(rem);
 95         }
 96         try {
 97             bb.put(src);
 98             bb.flip();
 99             // Do not update src until we see how many bytes were written
100             src.position(pos);
101 
102             int n = writeFromNativeBuffer(fd, bb, position, directIO, async, alignment, nd);
103             if (n > 0) {
104                 // now update src
105                 src.position(pos + n);
106             }
107             return n;
108         } finally {
109             Util.offerFirstTemporaryDirectBuffer(bb);
110         }
111     }
112 
113     private static int writeFromNativeBuffer(FileDescriptor fd, ByteBuffer bb,
114                                              long position, boolean directIO,
115                                              boolean async, int alignment,
116                                              NativeDispatcher nd)
117         throws IOException
118     {
119         int pos = bb.position();
120         int lim = bb.limit();
121         assert (pos <= lim);
122         int rem = (pos <= lim ? lim - pos : 0);
123 
124         if (directIO) {
125             Util.checkBufferPositionAligned(bb, pos, alignment);
126             Util.checkRemainingBufferSizeAligned(rem, alignment);
127         }
128 
129         int written = 0;
130         if (rem == 0)
131             return 0;
132         var handle = acquireScope(bb, async);
133         try {
134             if (position != -1) {
135                 written = nd.pwrite(fd, bufferAddress(bb) + pos, rem, position);
136             } else {
137                 written = nd.write(fd, bufferAddress(bb) + pos, rem);
138             }
139         } finally {
140             releaseScope(handle);
141         }
142         if (written > 0)
143             bb.position(pos + written);
144         return written;
145     }
146 
147     static long write(FileDescriptor fd, ByteBuffer[] bufs, boolean async,
148                       NativeDispatcher nd)
149         throws IOException
150     {
151         return write(fd, bufs, 0, bufs.length, false, async, -1, nd);
152     }
153 
154     static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
155                       NativeDispatcher nd)
156         throws IOException
157     {
158         return write(fd, bufs, offset, length, false, false, -1, nd);
159     }
160 
161     static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
162                       boolean direct, int alignment, NativeDispatcher nd)
163         throws IOException
164     {
165         return write(fd, bufs, offset, length, direct, false, alignment, nd);
166     }
167 
168     static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
169                       boolean directIO, boolean async,
170                       int alignment, NativeDispatcher nd)
171         throws IOException
172     {
173         IOVecWrapper vec = IOVecWrapper.get(length);
174 
175         boolean completed = false;
176         int iov_len = 0;
177         Runnable handleReleasers = null;
178         try {
179             // Iterate over buffers to populate native iovec array.
180             long writevLen = 0L;
181             int count = offset + length;
182             int i = offset;
183             while (i < count && iov_len < IOV_MAX && writevLen < WRITEV_MAX) {
184                 ByteBuffer buf = bufs[i];
185                 var h = acquireScope(buf, async);
186                 if (h != null) {
187                     handleReleasers = LinkedRunnable.of(Releaser.of(h), handleReleasers);
188                 }
189                 int pos = buf.position();
190                 int lim = buf.limit();
191                 assert (pos <= lim);
192                 int rem = (pos <= lim ? lim - pos : 0);
193                 if (directIO)
194                     Util.checkRemainingBufferSizeAligned(rem, alignment);
195 
196                 if (rem > 0) {
197                     long headroom = WRITEV_MAX - writevLen;
198                     if (headroom < rem)
199                         rem = (int)headroom;
200 
201                     vec.setBuffer(iov_len, buf, pos, rem);
202 
203                     // allocate shadow buffer to ensure I/O is done with direct buffer
204                     if (!(buf instanceof DirectBuffer)) {
205                         ByteBuffer shadow;
206                         if (directIO)
207                             shadow = Util.getTemporaryAlignedDirectBuffer(rem, alignment);
208                         else
209                             shadow = Util.getTemporaryDirectBuffer(rem);
210                         shadow.put(shadow.position(), buf, pos, rem);
211                         shadow.flip();
212                         vec.setShadow(iov_len, shadow);
213                         buf = shadow;
214                         pos = shadow.position();
215                     }
216 
217                     vec.putBase(iov_len, bufferAddress(buf) + pos);
218                     vec.putLen(iov_len, rem);
219                     iov_len++;
220                     writevLen += rem;
221                 }
222                 i++;
223             }
224             if (iov_len == 0)
225                 return 0L;
226 
227             long bytesWritten = nd.writev(fd, vec.address, iov_len);
228 
229             // Notify the buffers how many bytes were taken
230             long left = bytesWritten;
231             for (int j=0; j<iov_len; j++) {
232                 if (left > 0) {
233                     ByteBuffer buf = vec.getBuffer(j);
234                     int pos = vec.getPosition(j);
235                     int rem = vec.getRemaining(j);
236                     int n = (left > rem) ? rem : (int)left;
237                     buf.position(pos + n);
238                     left -= n;
239                 }
240                 // return shadow buffers to buffer pool
241                 ByteBuffer shadow = vec.getShadow(j);
242                 if (shadow != null)
243                     Util.offerLastTemporaryDirectBuffer(shadow);
244                 vec.clearRefs(j);
245             }
246 
247             completed = true;
248             return bytesWritten;
249 
250         } finally {
251             releaseScopes(handleReleasers);
252             // if an error occurred then clear refs to buffers and return any shadow
253             // buffers to cache
254             if (!completed) {
255                 for (int j=0; j<iov_len; j++) {
256                     ByteBuffer shadow = vec.getShadow(j);
257                     if (shadow != null)
258                         Util.offerLastTemporaryDirectBuffer(shadow);
259                     vec.clearRefs(j);
260                 }
261             }
262         }
263     }
264 
265     static int read(FileDescriptor fd, ByteBuffer dst, long position,
266                     NativeDispatcher nd)
267         throws IOException
268     {
269         return read(fd, dst, position, false, false, -1, nd);
270     }
271 
272     static int read(FileDescriptor fd, ByteBuffer dst, long position,
273                     boolean async, NativeDispatcher nd)
274         throws IOException
275     {
276         return read(fd, dst, position, false, async, -1, nd);
277     }
278 
279     static int read(FileDescriptor fd, ByteBuffer dst, long position,
280                     boolean directIO, int alignment, NativeDispatcher nd)
281         throws IOException
282     {
283         return read(fd, dst, position, directIO, false, alignment, nd);
284     }
285 
286     static int read(FileDescriptor fd, ByteBuffer dst, long position,
287                     boolean directIO, boolean async,
288                     int alignment, NativeDispatcher nd)
289         throws IOException
290     {
291         if (dst.isReadOnly())
292             throw new IllegalArgumentException("Read-only buffer");
293         if (dst instanceof DirectBuffer)
294             return readIntoNativeBuffer(fd, dst, position, directIO, async, alignment, nd);
295 
296         // Substitute a native buffer
297         ByteBuffer bb;
298         int rem = dst.remaining();
299         if (directIO) {
300             Util.checkRemainingBufferSizeAligned(rem, alignment);
301             bb = Util.getTemporaryAlignedDirectBuffer(rem, alignment);
302         } else {
303             bb = Util.getTemporaryDirectBuffer(rem);
304         }
305         try {
306             int n = readIntoNativeBuffer(fd, bb, position, directIO, async, alignment, nd);
307             bb.flip();
308             if (n > 0)
309                 dst.put(bb);
310             return n;
311         } finally {
312             Util.offerFirstTemporaryDirectBuffer(bb);
313         }
314     }
315 
316     private static int readIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
317                                             long position, boolean directIO,
318                                             boolean async, int alignment,
319                                             NativeDispatcher nd)
320         throws IOException
321     {
322         int pos = bb.position();
323         int lim = bb.limit();
324         assert (pos <= lim);
325         int rem = (pos <= lim ? lim - pos : 0);
326 
327         if (directIO) {
328             Util.checkBufferPositionAligned(bb, pos, alignment);
329             Util.checkRemainingBufferSizeAligned(rem, alignment);
330         }
331 
332         if (rem == 0)
333             return 0;
334         int n = 0;
335         var handle = acquireScope(bb, async);
336         try {
337             if (position != -1) {
338                 n = nd.pread(fd, bufferAddress(bb) + pos, rem, position);
339             } else {
340                 n = nd.read(fd, bufferAddress(bb) + pos, rem);
341             }
342         } finally {
343             releaseScope(handle);
344         }
345         if (n > 0)
346             bb.position(pos + n);
347         return n;
348     }
349 
350     static long read(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd)
351         throws IOException
352     {
353         return read(fd, bufs, 0, bufs.length, false, false, -1, nd);
354     }
355 
356     static long read(FileDescriptor fd, ByteBuffer[] bufs, boolean async,
357                      NativeDispatcher nd)
358         throws IOException
359     {
360         return read(fd, bufs, 0, bufs.length, false, async, -1, nd);
361     }
362 
363     static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
364                      NativeDispatcher nd)
365         throws IOException
366     {
367         return read(fd, bufs, offset, length, false, false, -1, nd);
368     }
369 
370     static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
371                      boolean directIO, int alignment, NativeDispatcher nd)
372 
373         throws IOException
374     {
375         return read(fd, bufs, offset, length, directIO, false, alignment, nd);
376     }
377 
378     static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
379                      boolean directIO, boolean async,
380                      int alignment, NativeDispatcher nd)
381 
382         throws IOException
383     {
384         IOVecWrapper vec = IOVecWrapper.get(length);
385 
386         boolean completed = false;
387         int iov_len = 0;
388         Runnable handleReleasers = null;
389         try {
390             // Iterate over buffers to populate native iovec array.
391             int count = offset + length;
392             int i = offset;
393             while (i < count && iov_len < IOV_MAX) {
394                 ByteBuffer buf = bufs[i];
395                 if (buf.isReadOnly())
396                     throw new IllegalArgumentException("Read-only buffer");
397                 var h = acquireScope(buf, async);
398                 if (h != null) {
399                     handleReleasers = LinkedRunnable.of(Releaser.of(h), handleReleasers);
400                 }
401                 int pos = buf.position();
402                 int lim = buf.limit();
403                 assert (pos <= lim);
404                 int rem = (pos <= lim ? lim - pos : 0);
405 
406                 if (directIO)
407                     Util.checkRemainingBufferSizeAligned(rem, alignment);
408 
409                 if (rem > 0) {
410                     vec.setBuffer(iov_len, buf, pos, rem);
411 
412                     // allocate shadow buffer to ensure I/O is done with direct buffer
413                     if (!(buf instanceof DirectBuffer)) {
414                         ByteBuffer shadow;
415                         if (directIO) {
416                             shadow = Util.getTemporaryAlignedDirectBuffer(rem, alignment);
417                         } else {
418                             shadow = Util.getTemporaryDirectBuffer(rem);
419                         }
420                         vec.setShadow(iov_len, shadow);
421                         buf = shadow;
422                         pos = shadow.position();
423                     }
424 
425                     vec.putBase(iov_len, bufferAddress(buf) + pos);
426                     vec.putLen(iov_len, rem);
427                     iov_len++;
428                 }
429                 i++;
430             }
431             if (iov_len == 0)
432                 return 0L;
433 
434             long bytesRead = nd.readv(fd, vec.address, iov_len);
435 
436             // Notify the buffers how many bytes were read
437             long left = bytesRead;
438             for (int j=0; j<iov_len; j++) {
439                 ByteBuffer shadow = vec.getShadow(j);
440                 if (left > 0) {
441                     ByteBuffer buf = vec.getBuffer(j);
442                     int rem = vec.getRemaining(j);
443                     int n = (left > rem) ? rem : (int)left;
444                     if (shadow == null) {
445                         int pos = vec.getPosition(j);
446                         buf.position(pos + n);
447                     } else {
448                         shadow.limit(shadow.position() + n);
449                         buf.put(shadow);
450                     }
451                     left -= n;
452                 }
453                 if (shadow != null)
454                     Util.offerLastTemporaryDirectBuffer(shadow);
455                 vec.clearRefs(j);
456             }
457 
458             completed = true;
459             return bytesRead;
460 
461         } finally {
462             releaseScopes(handleReleasers);
463             // if an error occurred then clear refs to buffers and return any shadow
464             // buffers to cache
465             if (!completed) {
466                 for (int j=0; j<iov_len; j++) {
467                     ByteBuffer shadow = vec.getShadow(j);
468                     if (shadow != null)
469                         Util.offerLastTemporaryDirectBuffer(shadow);
470                     vec.clearRefs(j);
471                 }
472             }
473         }
474     }
475 
476     private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
477 
478     static Scope.Handle acquireScope(ByteBuffer bb, boolean async) {
479         return NIO_ACCESS.acquireScope(bb, async);
480     }
481 
482     private static void releaseScope(Scope.Handle handle) {
483         if (handle == null)
484             return;
485         try {
486             handle.scope().release(handle);
487         } catch (Exception e) {
488             throw new IllegalStateException(e);
489         }
490     }
491 
492     static Runnable acquireScopes(ByteBuffer[] buffers) {
493         return acquireScopes(null, buffers);
494     }
495 
496     static Runnable acquireScopes(ByteBuffer buf, ByteBuffer[] buffers) {
497         if (buffers == null) {
498             assert buf != null;
499             return IOUtil.Releaser.ofNullable(IOUtil.acquireScope(buf, true));
500         } else {
501             assert buf == null;
502             Runnable handleReleasers = null;
503             for (var b : buffers) {
504                 var h = IOUtil.acquireScope(b, true);
505                 if (h != null) {
506                     handleReleasers = IOUtil.LinkedRunnable.of(IOUtil.Releaser.of(h), handleReleasers);
507                 }
508             }
509             return handleReleasers;
510         }
511     }
512 
513     static void releaseScopes(Runnable releasers) {
514         if (releasers != null)
515             releasers.run();
516     }
517 
518     static record LinkedRunnable(Runnable node, Runnable next)
519         implements Runnable
520     {
521         LinkedRunnable {
522             Objects.requireNonNull(node);
523         }
524         @Override
525         public void run() {
526             try {
527                 node.run();
528             } finally {
529                 if (next != null)
530                     next.run();
531             }
532         }
533         static LinkedRunnable of(Runnable first, Runnable second) {
534             return new LinkedRunnable(first, second);
535         }
536     }
537 
538     static record Releaser(Scope.Handle handle) implements Runnable {
539         Releaser { Objects.requireNonNull(handle) ; }
540         @Override public void run() { releaseScope(handle); }
541         static Runnable of(Scope.Handle handle) { return new Releaser(handle); }
542         static Runnable ofNullable(Scope.Handle handle) {
543             if (handle == null)
544                 return () -> { };
545             return new Releaser(handle);
546         }
547     }
548 
549     static long bufferAddress(ByteBuffer buf) {
550         return NIO_ACCESS.getBufferAddress(buf);
551     }
552 
553     public static FileDescriptor newFD(int i) {
554         FileDescriptor fd = new FileDescriptor();
555         setfdVal(fd, i);
556         return fd;
557     }
558 
559     static native boolean randomBytes(byte[] someBytes);
560 
561     /**
562      * Returns two file descriptors for a pipe encoded in a long.
563      * The read end of the pipe is returned in the high 32 bits,
564      * while the write end is returned in the low 32 bits.
565      */
566     static native long makePipe(boolean blocking) throws IOException;
567 
568     static native int write1(int fd, byte b) throws IOException;
569 
570     /**
571      * Read and discard all bytes.
572      */
573     static native boolean drain(int fd) throws IOException;
574 
575     /**
576      * Read and discard at most one byte
577      * @return the number of bytes read or IOS_INTERRUPTED
578      */
579     static native int drain1(int fd) throws IOException;
580 
581     public static native void configureBlocking(FileDescriptor fd,
582                                                 boolean blocking)
583         throws IOException;
584 
585     public static native int fdVal(FileDescriptor fd);
586 
587     static native void setfdVal(FileDescriptor fd, int value);
588 
589     static native int fdLimit();
590 
591     static native int iovMax();
592 
593     static native long writevMax();
594 
595     static native void initIDs();
596 
597     /**
598      * Used to trigger loading of native libraries
599      */
600     public static void load() { }
601 
602     static {
603         jdk.internal.loader.BootLoader.loadLibrary("net");
604         jdk.internal.loader.BootLoader.loadLibrary("nio");
605         initIDs();
606 
607         IOV_MAX = iovMax();
608         WRITEV_MAX = writevMax();
609     }
610 
611 }