From dbba4f5731dbc9b1cda651712fff2d50795bc3d8 Mon Sep 17 00:00:00 2001 From: Markus KARG Date: Sat, 9 Mar 2024 17:46:45 +0100 Subject: [PATCH] Skipping on Windows (unfortunately is an endless loop) --- .../classes/sun/nio/ch/SocketChannelImpl.java | 50 +++++++++++++++++++ src/java.base/unix/native/libnio/ch/IOUtil.c | 19 +++++-- .../classes/sun/nio/ch/SourceChannelImpl.java | 34 +++++++++++++ .../windows/native/libnio/ch/IOUtil.c | 30 +++++++++++ 4 files changed, 128 insertions(+), 5 deletions(-) diff --git a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java index 6c65a964a65f8..c08646249cef9 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -1625,4 +1625,54 @@ public String toString() { sb.append(']'); return sb.toString(); } + + /** + * Skips over and discards {@code n} bytes of data from this source + * channel. The {@code skip} method may, for a variety of reasons, end + * up skipping over some smaller number of bytes, possibly {@code 0}. + * This may result from any of a number of conditions; reaching end of file + * before {@code n} bytes have been skipped is only one possibility. + * The actual number of bytes skipped is returned. If {@code n} is + * negative, the {@code skip} method for class {@code SocketChannelImpl} always + * returns 0, and no bytes are skipped. Subclasses may handle the negative + * value differently. + * + * @implSpec + * The {@code skip} method implementation of this class creates a + * byte array and then repeatedly reads into it until {@code n} bytes + * have been read or the end of the stream has been reached. Subclasses are + * encouraged to provide a more efficient implementation of this method. + * For instance, the implementation may depend on the ability to seek. + * + * @param n the number of bytes to be skipped. + * @return the actual number of bytes skipped which might be zero. + * @throws IOException if an I/O error occurs. + * @see java.io.InputStream#skipNBytes(long) + */ + public long skip(long n) throws IOException { + if (n < 1) + return 0; + + readLock.lock(); + try { + boolean blocking = isBlocking(); + long ns = 0; + try { + beginRead(blocking); + configureSocketNonBlockingIfVirtualThread(); + ns = IOUtil.drainN(fdVal, n); + if (blocking) + while (IOStatus.okayToRetry(ns) && isOpen()) { + park(Net.POLLIN); + ns = IOUtil.drainN(fdVal, n); + } + } finally { + endRead(blocking, ns > 0); + assert IOStatus.check(ns); + } + return IOStatus.normalize(ns); + } finally { + readLock.unlock(); + } + } } diff --git a/src/java.base/unix/native/libnio/ch/IOUtil.c b/src/java.base/unix/native/libnio/ch/IOUtil.c index 5a468a3fed561..f1b8e16eef4a3 100644 --- a/src/java.base/unix/native/libnio/ch/IOUtil.c +++ b/src/java.base/unix/native/libnio/ch/IOUtil.c @@ -158,17 +158,26 @@ Java_sun_nio_ch_IOUtil_drainN(JNIEnv *env, jclass cl, jint fd, jlong n) if (n < 1) return 0; - const long bs = n < MAX_SKIP_BUFFER_SIZE ? n : MAX_SKIP_BUFFER_SIZE; + const long bs = n < MAX_SKIP_BUFFER_SIZE ? (long) n : MAX_SKIP_BUFFER_SIZE; char buf[bs]; jlong tn = 0; for (;;) { const jlong remaining = n - tn; - const ssize_t count = remaining < bs ? remaining : bs; + const ssize_t count = remaining < bs ? (ssize_t) remaining : bs; const ssize_t nr = read(fd, buf, count); - tn += nr; - if ((nr < 0) && (errno != EAGAIN && errno != EWOULDBLOCK)) - JNU_ThrowIOExceptionWithLastError(env, "DrainN"); + if (nr < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return tn; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } else { + JNU_ThrowIOExceptionWithLastError(env, "read"); + return IOS_THROWN; + } + } + if (nr > 0) + tn += nr; if (nr == bs) continue; return tn; diff --git a/src/java.base/windows/classes/sun/nio/ch/SourceChannelImpl.java b/src/java.base/windows/classes/sun/nio/ch/SourceChannelImpl.java index f4e7510447316..0bd0da07bdec9 100644 --- a/src/java.base/windows/classes/sun/nio/ch/SourceChannelImpl.java +++ b/src/java.base/windows/classes/sun/nio/ch/SourceChannelImpl.java @@ -139,4 +139,38 @@ public long read(ByteBuffer[] dsts) throws IOException { } } + /** + * Skips over and discards {@code n} bytes of data from this source + * channel. The {@code skip} method may, for a variety of reasons, end + * up skipping over some smaller number of bytes, possibly {@code 0}. + * This may result from any of a number of conditions; reaching end of file + * before {@code n} bytes have been skipped is only one possibility. + * The actual number of bytes skipped is returned. If {@code n} is + * negative, the {@code skip} method for class {@code SourceChannelImpl} always + * returns 0, and no bytes are skipped. Subclasses may handle the negative + * value differently. + * + * @implSpec + * The {@code skip} method implementation of this class creates a + * byte array and then repeatedly reads into it until {@code n} bytes + * have been read or the end of the stream has been reached. Subclasses are + * encouraged to provide a more efficient implementation of this method. + * For instance, the implementation may depend on the ability to seek. + * + * @param n the number of bytes to be skipped. + * @return the actual number of bytes skipped which might be zero. + * @throws IOException if an I/O error occurs. + * @see java.io.InputStream#skipNBytes(long) + */ + public long skip(long n) throws IOException { + if (n < 1) + return 0; + + try { + return ((SocketChannelImpl) sc).skip(n); + } catch (AsynchronousCloseException x) { + close(); + throw x; + } + } } diff --git a/src/java.base/windows/native/libnio/ch/IOUtil.c b/src/java.base/windows/native/libnio/ch/IOUtil.c index 850c237d9e908..59697eda631c2 100644 --- a/src/java.base/windows/native/libnio/ch/IOUtil.c +++ b/src/java.base/windows/native/libnio/ch/IOUtil.c @@ -43,6 +43,10 @@ static jfieldID handle_fdID; /* field id for jint 'fd' in java.io.FileDescriptor used for socket fds */ static jfieldID fd_fdID; +// MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to +// use when skipping. +static const int MAX_SKIP_BUFFER_SIZE = 4096; + JNIEXPORT jboolean JNICALL Java_sun_security_provider_NativeSeedGenerator_nativeGenerateSeed (JNIEnv *env, jclass clazz, jbyteArray randArray); @@ -174,6 +178,32 @@ Java_sun_nio_ch_IOUtil_drain(JNIEnv *env, jclass cl, jint fd) } } +JNIEXPORT jlong JNICALL +Java_sun_nio_ch_IOUtil_drainN(JNIEnv *env, jclass cl, jint fd, jlong nt) +{ + if (nt < 1) + return 0; + + char buf[4096]; + jlong readBytes = 0; + for (;;) { + const jlong remaining = nt - readBytes; + const int count = remaining < sizeof(buf) ? (int) remaining : sizeof(buf); + int n = recv((SOCKET) fd, buf, count, 0); + if (n == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + JNU_ThrowIOExceptionWithLastError(env, "recv failed"); + } + return readBytes; + } + if (n <= 0) + return readBytes; + readBytes += n; + if (n < (int)sizeof(buf)) + return readBytes; + } +} + JNIEXPORT jint JNICALL Java_sun_nio_ch_IOUtil_write1(JNIEnv *env, jclass cl, jint fd, jbyte b) {