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