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