1 /*
  2  * Copyright (c) 2000, 2025, 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.foreign.MemorySessionImpl;
 35 
 36 /**
 37  * File-descriptor based I/O utilities that are shared by NIO classes.
 38  */
 39 
 40 public final 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         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(bb);
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                 acquireScope(buf, async);
186                 if (NIO_ACCESS.hasSession(buf)) {
187                     handleReleasers = LinkedRunnable.of(Releaser.of(buf), 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             vec.release();
263         }
264     }
265 
266     static int read(FileDescriptor fd, ByteBuffer dst, long position,
267                     NativeDispatcher nd)
268         throws IOException
269     {
270         return read(fd, dst, position, false, false, -1, nd);
271     }
272 
273     static int read(FileDescriptor fd, ByteBuffer dst, long position,
274                     boolean async, NativeDispatcher nd)
275         throws IOException
276     {
277         return read(fd, dst, position, false, async, -1, nd);
278     }
279 
280     static int read(FileDescriptor fd, ByteBuffer dst, long position,
281                     boolean directIO, int alignment, NativeDispatcher nd)
282         throws IOException
283     {
284         return read(fd, dst, position, directIO, false, alignment, nd);
285     }
286 
287     static int read(FileDescriptor fd, ByteBuffer dst, long position,
288                     boolean directIO, boolean async,
289                     int alignment, NativeDispatcher nd)
290         throws IOException
291     {
292         if (dst.isReadOnly())
293             throw new IllegalArgumentException("Read-only buffer");
294         if (dst instanceof DirectBuffer)
295             return readIntoNativeBuffer(fd, dst, position, directIO, async, alignment, nd);
296 
297         // Substitute a native buffer
298         ByteBuffer bb;
299         int rem = dst.remaining();
300         if (directIO) {
301             Util.checkRemainingBufferSizeAligned(rem, alignment);
302             bb = Util.getTemporaryAlignedDirectBuffer(rem, alignment);
303         } else {
304             bb = Util.getTemporaryDirectBuffer(rem);
305         }
306         try {
307             int n = readIntoNativeBuffer(fd, bb, position, directIO, async, alignment, nd);
308             bb.flip();
309             if (n > 0)
310                 dst.put(bb);
311             return n;
312         } finally {
313             Util.offerFirstTemporaryDirectBuffer(bb);
314         }
315     }
316 
317     private static int readIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
318                                             long position, boolean directIO,
319                                             boolean async, int alignment,
320                                             NativeDispatcher nd)
321         throws IOException
322     {
323         int pos = bb.position();
324         int lim = bb.limit();
325         assert (pos <= lim);
326         int rem = (pos <= lim ? lim - pos : 0);
327 
328         if (directIO) {
329             Util.checkBufferPositionAligned(bb, pos, alignment);
330             Util.checkRemainingBufferSizeAligned(rem, alignment);
331         }
332 
333         if (rem == 0)
334             return 0;
335         int n = 0;
336         acquireScope(bb, async);
337         try {
338             if (position != -1) {
339                 n = nd.pread(fd, bufferAddress(bb) + pos, rem, position);
340             } else {
341                 n = nd.read(fd, bufferAddress(bb) + pos, rem);
342             }
343         } finally {
344             releaseScope(bb);
345         }
346         if (n > 0)
347             bb.position(pos + n);
348         return n;
349     }
350 
351     static long read(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd)
352         throws IOException
353     {
354         return read(fd, bufs, 0, bufs.length, false, false, -1, nd);
355     }
356 
357     static long read(FileDescriptor fd, ByteBuffer[] bufs, boolean async,
358                      NativeDispatcher nd)
359         throws IOException
360     {
361         return read(fd, bufs, 0, bufs.length, false, async, -1, nd);
362     }
363 
364     static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
365                      NativeDispatcher nd)
366         throws IOException
367     {
368         return read(fd, bufs, offset, length, false, false, -1, nd);
369     }
370 
371     static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
372                      boolean directIO, int alignment, NativeDispatcher nd)
373 
374         throws IOException
375     {
376         return read(fd, bufs, offset, length, directIO, false, alignment, nd);
377     }
378 
379     static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
380                      boolean directIO, boolean async,
381                      int alignment, NativeDispatcher nd)
382 
383         throws IOException
384     {
385         IOVecWrapper vec = IOVecWrapper.get(length);
386 
387         boolean completed = false;
388         int iov_len = 0;
389         Runnable handleReleasers = null;
390         try {
391             // Iterate over buffers to populate native iovec array.
392             int count = offset + length;
393             int i = offset;
394             while (i < count && iov_len < IOV_MAX) {
395                 ByteBuffer buf = bufs[i];
396                 if (buf.isReadOnly())
397                     throw new IllegalArgumentException("Read-only buffer");
398                 acquireScope(buf, async);
399                 if (NIO_ACCESS.hasSession(buf)) {
400                     handleReleasers = LinkedRunnable.of(Releaser.of(buf), handleReleasers);
401                 }
402                 int pos = buf.position();
403                 int lim = buf.limit();
404                 assert (pos <= lim);
405                 int rem = (pos <= lim ? lim - pos : 0);
406 
407                 if (directIO)
408                     Util.checkRemainingBufferSizeAligned(rem, alignment);
409 
410                 if (rem > 0) {
411                     vec.setBuffer(iov_len, buf, pos, rem);
412 
413                     // allocate shadow buffer to ensure I/O is done with direct buffer
414                     if (!(buf instanceof DirectBuffer)) {
415                         ByteBuffer shadow;
416                         if (directIO) {
417                             shadow = Util.getTemporaryAlignedDirectBuffer(rem, alignment);
418                         } else {
419                             shadow = Util.getTemporaryDirectBuffer(rem);
420                         }
421                         vec.setShadow(iov_len, shadow);
422                         buf = shadow;
423                         pos = shadow.position();
424                     }
425 
426                     vec.putBase(iov_len, bufferAddress(buf) + pos);
427                     vec.putLen(iov_len, rem);
428                     iov_len++;
429                 }
430                 i++;
431             }
432             if (iov_len == 0)
433                 return 0L;
434 
435             long bytesRead = nd.readv(fd, vec.address, iov_len);
436 
437             // Notify the buffers how many bytes were read
438             long left = bytesRead;
439             for (int j=0; j<iov_len; j++) {
440                 ByteBuffer shadow = vec.getShadow(j);
441                 if (left > 0) {
442                     ByteBuffer buf = vec.getBuffer(j);
443                     int rem = vec.getRemaining(j);
444                     int n = (left > rem) ? rem : (int)left;
445                     if (shadow == null) {
446                         int pos = vec.getPosition(j);
447                         buf.position(pos + n);
448                     } else {
449                         shadow.limit(shadow.position() + n);
450                         buf.put(shadow);
451                     }
452                     left -= n;
453                 }
454                 if (shadow != null)
455                     Util.offerLastTemporaryDirectBuffer(shadow);
456                 vec.clearRefs(j);
457             }
458 
459             completed = true;
460             return bytesRead;
461 
462         } finally {
463             releaseScopes(handleReleasers);
464             // if an error occurred then clear refs to buffers and return any shadow
465             // buffers to cache
466             if (!completed) {
467                 for (int j=0; j<iov_len; j++) {
468                     ByteBuffer shadow = vec.getShadow(j);
469                     if (shadow != null)
470                         Util.offerLastTemporaryDirectBuffer(shadow);
471                     vec.clearRefs(j);
472                 }
473             }
474             vec.release();
475         }
476     }
477 
478     private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
479 
480     static void acquireScope(ByteBuffer bb, boolean async) {
481         if (async && NIO_ACCESS.isThreadConfined(bb)) {
482             throw new IllegalArgumentException("Buffer is thread confined");
483         }
484         NIO_ACCESS.acquireSession(bb);
485     }
486 
487     static void releaseScope(ByteBuffer bb) {
488         try {
489             NIO_ACCESS.releaseSession(bb);
490         } catch (Exception e) {
491             throw new IllegalStateException(e);
492         }
493     }
494 
495     static Runnable acquireScopes(ByteBuffer[] buffers) {
496         return acquireScopes(null, buffers);
497     }
498 
499     static Runnable acquireScopes(ByteBuffer buf, ByteBuffer[] buffers) {
500         if (buffers == null) {
501             assert buf != null;
502             IOUtil.acquireScope(buf, true);
503             return IOUtil.Releaser.of(buf);
504         } else {
505             assert buf == null;
506             Runnable handleReleasers = null;
507             for (var b : buffers) {
508                 IOUtil.acquireScope(b, true);
509                 handleReleasers = IOUtil.LinkedRunnable.of(IOUtil.Releaser.of(b), handleReleasers);
510             }
511             return handleReleasers;
512         }
513     }
514 
515     static void releaseScopes(Runnable releasers) {
516         if (releasers != null)
517             releasers.run();
518     }
519 
520     record LinkedRunnable(Runnable node, Runnable next) implements Runnable {
521         LinkedRunnable {
522             Objects.requireNonNull(node);
523         }
524 
525         @Override
526         public void run() {
527             try {
528                 node.run();
529             } finally {
530                 if (next != null)
531                     next.run();
532             }
533         }
534 
535         static LinkedRunnable of(Runnable first, Runnable second) {
536             return new LinkedRunnable(first, second);
537         }
538     }
539 
540     record Releaser(ByteBuffer bb) implements Runnable {
541         Releaser {
542             Objects.requireNonNull(bb);
543         }
544 
545         @Override
546         public void run() {
547             releaseScope(bb);
548         }
549 
550         static Runnable of(ByteBuffer bb) {
551             return NIO_ACCESS.hasSession(bb)
552                     ? new Releaser(bb)
553                     : () -> {};
554         }
555 
556     }
557 
558     static long bufferAddress(ByteBuffer buf) {
559         return NIO_ACCESS.getBufferAddress(buf);
560     }
561 
562     public static FileDescriptor newFD(int i) {
563         FileDescriptor fd = new FileDescriptor();
564         setfdVal(fd, i);
565         return fd;
566     }
567 
568     static native boolean randomBytes(byte[] someBytes);
569 
570     /**
571      * Returns two file descriptors for a pipe encoded in a long.
572      * The read end of the pipe is returned in the high 32 bits,
573      * while the write end is returned in the low 32 bits.
574      */
575     static native long makePipe(boolean blocking) throws IOException;
576 
577     static native int write1(int fd, byte b) throws IOException;
578 
579     /**
580      * Read and discard all bytes.
581      */
582     static native boolean drain(int fd) throws IOException;
583 
584     /**
585      * Read and discard at most one byte
586      * @return the number of bytes read or IOS_INTERRUPTED
587      */
588     static native int drain1(int fd) throws IOException;
589 
590     public static native void configureBlocking(FileDescriptor fd,
591                                                 boolean blocking)
592         throws IOException;
593 
594     public static native int fdVal(FileDescriptor fd);
595 
596     static native void setfdVal(FileDescriptor fd, int value);
597 
598     static native int fdLimit();
599 
600     static native int iovMax();
601 
602     static native long writevMax();
603 
604     static native void initIDs();
605 
606     /**
607      * Used to trigger loading of native libraries
608      */
609     public static void load() { }
610 
611     static {
612         jdk.internal.loader.BootLoader.loadLibrary("net");
613         jdk.internal.loader.BootLoader.loadLibrary("nio");
614         initIDs();
615 
616         IOV_MAX = iovMax();
617         WRITEV_MAX = writevMax();
618     }
619 
620 }