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 }