Path: blob/master/src/java.base/share/classes/sun/nio/ch/IOUtil.java
41159 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.ch;2627import java.io.FileDescriptor;28import java.io.IOException;29import java.nio.ByteBuffer;30import java.util.Objects;31import jdk.internal.access.JavaNioAccess;32import jdk.internal.access.SharedSecrets;33import jdk.internal.misc.ScopedMemoryAccess.Scope;3435/**36* File-descriptor based I/O utilities that are shared by NIO classes.37*/3839public class IOUtil {4041/**42* Max number of iovec structures that readv/writev supports43*/44static final int IOV_MAX;4546private IOUtil() { } // No instantiation4748static int write(FileDescriptor fd, ByteBuffer src, long position,49NativeDispatcher nd)50throws IOException51{52return write(fd, src, position, false, false, -1, nd);53}5455static int write(FileDescriptor fd, ByteBuffer src, long position,56boolean async, NativeDispatcher nd)57throws IOException58{59return write(fd, src, position, false, async, -1, nd);60}6162static int write(FileDescriptor fd, ByteBuffer src, long position,63boolean directIO, int alignment, NativeDispatcher nd)64throws IOException65{66return write(fd, src, position, directIO, false, alignment, nd);67}6869static int write(FileDescriptor fd, ByteBuffer src, long position,70boolean directIO, boolean async, int alignment,71NativeDispatcher nd)72throws IOException73{74if (src instanceof DirectBuffer) {75return writeFromNativeBuffer(fd, src, position, directIO, async, alignment, nd);76}7778// Substitute a native buffer79int pos = src.position();80int lim = src.limit();81assert (pos <= lim);82int rem = (pos <= lim ? lim - pos : 0);83ByteBuffer bb;84if (directIO) {85Util.checkRemainingBufferSizeAligned(rem, alignment);86bb = Util.getTemporaryAlignedDirectBuffer(rem, alignment);87} else {88bb = Util.getTemporaryDirectBuffer(rem);89}90try {91bb.put(src);92bb.flip();93// Do not update src until we see how many bytes were written94src.position(pos);9596int n = writeFromNativeBuffer(fd, bb, position, directIO, async, alignment, nd);97if (n > 0) {98// now update src99src.position(pos + n);100}101return n;102} finally {103Util.offerFirstTemporaryDirectBuffer(bb);104}105}106107private static int writeFromNativeBuffer(FileDescriptor fd, ByteBuffer bb,108long position, boolean directIO,109boolean async, int alignment,110NativeDispatcher nd)111throws IOException112{113int pos = bb.position();114int lim = bb.limit();115assert (pos <= lim);116int rem = (pos <= lim ? lim - pos : 0);117118if (directIO) {119Util.checkBufferPositionAligned(bb, pos, alignment);120Util.checkRemainingBufferSizeAligned(rem, alignment);121}122123int written = 0;124if (rem == 0)125return 0;126var handle = acquireScope(bb, async);127try {128if (position != -1) {129written = nd.pwrite(fd, bufferAddress(bb) + pos, rem, position);130} else {131written = nd.write(fd, bufferAddress(bb) + pos, rem);132}133} finally {134releaseScope(handle);135}136if (written > 0)137bb.position(pos + written);138return written;139}140141static long write(FileDescriptor fd, ByteBuffer[] bufs, boolean async,142NativeDispatcher nd)143throws IOException144{145return write(fd, bufs, 0, bufs.length, false, async, -1, nd);146}147148static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,149NativeDispatcher nd)150throws IOException151{152return write(fd, bufs, offset, length, false, false, -1, nd);153}154155static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,156boolean direct, int alignment, NativeDispatcher nd)157throws IOException158{159return write(fd, bufs, offset, length, direct, false, alignment, nd);160}161162static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,163boolean directIO, boolean async,164int alignment, NativeDispatcher nd)165throws IOException166{167IOVecWrapper vec = IOVecWrapper.get(length);168169boolean completed = false;170int iov_len = 0;171Runnable handleReleasers = null;172try {173// Iterate over buffers to populate native iovec array.174int count = offset + length;175int i = offset;176while (i < count && iov_len < IOV_MAX) {177ByteBuffer buf = bufs[i];178var h = acquireScope(buf, async);179if (h != null) {180handleReleasers = LinkedRunnable.of(Releaser.of(h), handleReleasers);181}182int pos = buf.position();183int lim = buf.limit();184assert (pos <= lim);185int rem = (pos <= lim ? lim - pos : 0);186if (directIO)187Util.checkRemainingBufferSizeAligned(rem, alignment);188189if (rem > 0) {190vec.setBuffer(iov_len, buf, pos, rem);191192// allocate shadow buffer to ensure I/O is done with direct buffer193if (!(buf instanceof DirectBuffer)) {194ByteBuffer shadow;195if (directIO)196shadow = Util.getTemporaryAlignedDirectBuffer(rem, alignment);197else198shadow = Util.getTemporaryDirectBuffer(rem);199shadow.put(buf);200shadow.flip();201vec.setShadow(iov_len, shadow);202buf.position(pos); // temporarily restore position in user buffer203buf = shadow;204pos = shadow.position();205}206207vec.putBase(iov_len, bufferAddress(buf) + pos);208vec.putLen(iov_len, rem);209iov_len++;210}211i++;212}213if (iov_len == 0)214return 0L;215216long bytesWritten = nd.writev(fd, vec.address, iov_len);217218// Notify the buffers how many bytes were taken219long left = bytesWritten;220for (int j=0; j<iov_len; j++) {221if (left > 0) {222ByteBuffer buf = vec.getBuffer(j);223int pos = vec.getPosition(j);224int rem = vec.getRemaining(j);225int n = (left > rem) ? rem : (int)left;226buf.position(pos + n);227left -= n;228}229// return shadow buffers to buffer pool230ByteBuffer shadow = vec.getShadow(j);231if (shadow != null)232Util.offerLastTemporaryDirectBuffer(shadow);233vec.clearRefs(j);234}235236completed = true;237return bytesWritten;238239} finally {240releaseScopes(handleReleasers);241// if an error occurred then clear refs to buffers and return any shadow242// buffers to cache243if (!completed) {244for (int j=0; j<iov_len; j++) {245ByteBuffer shadow = vec.getShadow(j);246if (shadow != null)247Util.offerLastTemporaryDirectBuffer(shadow);248vec.clearRefs(j);249}250}251}252}253254static int read(FileDescriptor fd, ByteBuffer dst, long position,255NativeDispatcher nd)256throws IOException257{258return read(fd, dst, position, false, false, -1, nd);259}260261static int read(FileDescriptor fd, ByteBuffer dst, long position,262boolean async, NativeDispatcher nd)263throws IOException264{265return read(fd, dst, position, false, async, -1, nd);266}267268static int read(FileDescriptor fd, ByteBuffer dst, long position,269boolean directIO, int alignment, NativeDispatcher nd)270throws IOException271{272return read(fd, dst, position, directIO, false, alignment, nd);273}274275static int read(FileDescriptor fd, ByteBuffer dst, long position,276boolean directIO, boolean async,277int alignment, NativeDispatcher nd)278throws IOException279{280if (dst.isReadOnly())281throw new IllegalArgumentException("Read-only buffer");282if (dst instanceof DirectBuffer)283return readIntoNativeBuffer(fd, dst, position, directIO, async, alignment, nd);284285// Substitute a native buffer286ByteBuffer bb;287int rem = dst.remaining();288if (directIO) {289Util.checkRemainingBufferSizeAligned(rem, alignment);290bb = Util.getTemporaryAlignedDirectBuffer(rem, alignment);291} else {292bb = Util.getTemporaryDirectBuffer(rem);293}294try {295int n = readIntoNativeBuffer(fd, bb, position, directIO, async, alignment, nd);296bb.flip();297if (n > 0)298dst.put(bb);299return n;300} finally {301Util.offerFirstTemporaryDirectBuffer(bb);302}303}304305private static int readIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,306long position, boolean directIO,307boolean async, int alignment,308NativeDispatcher nd)309throws IOException310{311int pos = bb.position();312int lim = bb.limit();313assert (pos <= lim);314int rem = (pos <= lim ? lim - pos : 0);315316if (directIO) {317Util.checkBufferPositionAligned(bb, pos, alignment);318Util.checkRemainingBufferSizeAligned(rem, alignment);319}320321if (rem == 0)322return 0;323int n = 0;324var handle = acquireScope(bb, async);325try {326if (position != -1) {327n = nd.pread(fd, bufferAddress(bb) + pos, rem, position);328} else {329n = nd.read(fd, bufferAddress(bb) + pos, rem);330}331} finally {332releaseScope(handle);333}334if (n > 0)335bb.position(pos + n);336return n;337}338339static long read(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd)340throws IOException341{342return read(fd, bufs, 0, bufs.length, false, false, -1, nd);343}344345static long read(FileDescriptor fd, ByteBuffer[] bufs, boolean async,346NativeDispatcher nd)347throws IOException348{349return read(fd, bufs, 0, bufs.length, false, async, -1, nd);350}351352static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,353NativeDispatcher nd)354throws IOException355{356return read(fd, bufs, offset, length, false, false, -1, nd);357}358359static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,360boolean directIO, int alignment, NativeDispatcher nd)361362throws IOException363{364return read(fd, bufs, offset, length, directIO, false, alignment, nd);365}366367static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,368boolean directIO, boolean async,369int alignment, NativeDispatcher nd)370371throws IOException372{373IOVecWrapper vec = IOVecWrapper.get(length);374375boolean completed = false;376int iov_len = 0;377Runnable handleReleasers = null;378try {379// Iterate over buffers to populate native iovec array.380int count = offset + length;381int i = offset;382while (i < count && iov_len < IOV_MAX) {383ByteBuffer buf = bufs[i];384if (buf.isReadOnly())385throw new IllegalArgumentException("Read-only buffer");386var h = acquireScope(buf, async);387if (h != null) {388handleReleasers = LinkedRunnable.of(Releaser.of(h), handleReleasers);389}390int pos = buf.position();391int lim = buf.limit();392assert (pos <= lim);393int rem = (pos <= lim ? lim - pos : 0);394395if (directIO)396Util.checkRemainingBufferSizeAligned(rem, alignment);397398if (rem > 0) {399vec.setBuffer(iov_len, buf, pos, rem);400401// allocate shadow buffer to ensure I/O is done with direct buffer402if (!(buf instanceof DirectBuffer)) {403ByteBuffer shadow;404if (directIO) {405shadow = Util.getTemporaryAlignedDirectBuffer(rem, alignment);406} else {407shadow = Util.getTemporaryDirectBuffer(rem);408}409vec.setShadow(iov_len, shadow);410buf = shadow;411pos = shadow.position();412}413414vec.putBase(iov_len, bufferAddress(buf) + pos);415vec.putLen(iov_len, rem);416iov_len++;417}418i++;419}420if (iov_len == 0)421return 0L;422423long bytesRead = nd.readv(fd, vec.address, iov_len);424425// Notify the buffers how many bytes were read426long left = bytesRead;427for (int j=0; j<iov_len; j++) {428ByteBuffer shadow = vec.getShadow(j);429if (left > 0) {430ByteBuffer buf = vec.getBuffer(j);431int rem = vec.getRemaining(j);432int n = (left > rem) ? rem : (int)left;433if (shadow == null) {434int pos = vec.getPosition(j);435buf.position(pos + n);436} else {437shadow.limit(shadow.position() + n);438buf.put(shadow);439}440left -= n;441}442if (shadow != null)443Util.offerLastTemporaryDirectBuffer(shadow);444vec.clearRefs(j);445}446447completed = true;448return bytesRead;449450} finally {451releaseScopes(handleReleasers);452// if an error occurred then clear refs to buffers and return any shadow453// buffers to cache454if (!completed) {455for (int j=0; j<iov_len; j++) {456ByteBuffer shadow = vec.getShadow(j);457if (shadow != null)458Util.offerLastTemporaryDirectBuffer(shadow);459vec.clearRefs(j);460}461}462}463}464465private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();466467static Scope.Handle acquireScope(ByteBuffer bb, boolean async) {468return NIO_ACCESS.acquireScope(bb, async);469}470471private static void releaseScope(Scope.Handle handle) {472if (handle == null)473return;474try {475handle.scope().release(handle);476} catch (Exception e) {477throw new IllegalStateException(e);478}479}480481static Runnable acquireScopes(ByteBuffer[] buffers) {482return acquireScopes(null, buffers);483}484485static Runnable acquireScopes(ByteBuffer buf, ByteBuffer[] buffers) {486if (buffers == null) {487assert buf != null;488return IOUtil.Releaser.ofNullable(IOUtil.acquireScope(buf, true));489} else {490assert buf == null;491Runnable handleReleasers = null;492for (var b : buffers) {493var h = IOUtil.acquireScope(b, true);494if (h != null) {495handleReleasers = IOUtil.LinkedRunnable.of(IOUtil.Releaser.of(h), handleReleasers);496}497}498return handleReleasers;499}500}501502static void releaseScopes(Runnable releasers) {503if (releasers != null)504releasers.run();505}506507static record LinkedRunnable(Runnable node, Runnable next)508implements Runnable509{510LinkedRunnable {511Objects.requireNonNull(node);512}513@Override514public void run() {515try {516node.run();517} finally {518if (next != null)519next.run();520}521}522static LinkedRunnable of(Runnable first, Runnable second) {523return new LinkedRunnable(first, second);524}525}526527static record Releaser(Scope.Handle handle) implements Runnable {528Releaser { Objects.requireNonNull(handle) ; }529@Override public void run() { releaseScope(handle); }530static Runnable of(Scope.Handle handle) { return new Releaser(handle); }531static Runnable ofNullable(Scope.Handle handle) {532if (handle == null)533return () -> { };534return new Releaser(handle);535}536}537538static long bufferAddress(ByteBuffer buf) {539return NIO_ACCESS.getBufferAddress(buf);540}541542public static FileDescriptor newFD(int i) {543FileDescriptor fd = new FileDescriptor();544setfdVal(fd, i);545return fd;546}547548static native boolean randomBytes(byte[] someBytes);549550/**551* Returns two file descriptors for a pipe encoded in a long.552* The read end of the pipe is returned in the high 32 bits,553* while the write end is returned in the low 32 bits.554*/555static native long makePipe(boolean blocking) throws IOException;556557static native int write1(int fd, byte b) throws IOException;558559/**560* Read and discard all bytes.561*/562static native boolean drain(int fd) throws IOException;563564/**565* Read and discard at most one byte566* @return the number of bytes read or IOS_INTERRUPTED567*/568static native int drain1(int fd) throws IOException;569570public static native void configureBlocking(FileDescriptor fd,571boolean blocking)572throws IOException;573574public static native int fdVal(FileDescriptor fd);575576static native void setfdVal(FileDescriptor fd, int value);577578static native int fdLimit();579580static native int iovMax();581582static native void initIDs();583584/**585* Used to trigger loading of native libraries586*/587public static void load() { }588589static {590jdk.internal.loader.BootLoader.loadLibrary("net");591jdk.internal.loader.BootLoader.loadLibrary("nio");592initIDs();593594IOV_MAX = iovMax();595}596597}598599600