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 }