From c7822e32c81493cef4533d0c617a868c3d5cdeb5 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 25 Oct 2025 22:40:52 -0400 Subject: [PATCH] Non-blocking read on Windows --- ...csocket_JNIWin32NamedPipeLibraryProvider.c | 26 ++++++++-- ...csocket_JNIWin32NamedPipeLibraryProvider.h | 4 +- .../JNAWin32NamedPipeLibraryProvider.java | 19 +++++--- .../JNIWin32NamedPipeLibraryProvider.java | 14 ++++-- .../scalasbt/ipcsocket/SocketChannels.java | 45 ++++++++++-------- .../ipcsocket/Win32NamedPipeLibrary.java | 2 + .../Win32NamedPipeLibraryProvider.java | 3 +- .../ipcsocket/Win32NamedPipeSocket.java | 3 +- .../resources/win32/x86_64/sbtipcsocket.dll | Bin 440678 -> 441398 bytes .../scalasbt/ipcsocket/SocketChannelTest.java | 4 +- 10 files changed, 81 insertions(+), 39 deletions(-) diff --git a/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.c b/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.c index ce1d006..198a948 100644 --- a/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.c +++ b/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.c @@ -18,6 +18,15 @@ (*env)->ThrowNew(env, exClass, _buf); \ } \ } while (0); +#define THROW_SOCKET_TIMEOUT(prefix, ...) \ + do { \ + char _buf[1024]; \ + snprintf(_buf, 1024, prefix ? prefix : "%s", __VA_ARGS__); \ + jclass exClass = (*env)->FindClass(env, "java/net/SocketTimeoutException"); \ + if (exClass != NULL) { \ + (*env)->ThrowNew(env, exClass, _buf); \ + } \ + } while (0); #define FILL_ERROR(prefix, buf) \ do { \ @@ -123,8 +132,8 @@ jlong JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_PeekNamedPipeNative( UNUSED JNIEnv *env, UNUSED jobject object, jlong handlePointer) { DWORD n = 0; - BOOL immediate = PeekNamedPipe((HANDLE)handlePointer, NULL, 0, NULL, &n, NULL); - if (!immediate) { + BOOL ok = PeekNamedPipe((HANDLE)handlePointer, NULL, 0, NULL, &n, NULL); + if (!ok) { if (GetLastError() != ERROR_IO_PENDING) { char buf[256]; FILL_ERROR("PeekNamedPipe() failed: %s (error code %ld)", buf); @@ -137,7 +146,8 @@ Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_PeekNamedPipeNative jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_readNative( JNIEnv *env, UNUSED jobject object, jlong waitable, jlong hFile, - jbyteArray buffer, jint offset, jint length, jboolean strict) { + jbyteArray buffer, jint offset, jint length, jboolean strict, + jint timeoutMillis) { HANDLE handle = (HANDLE)hFile; OVERLAPPED olap = {0}; olap.hEvent = (HANDLE)waitable; @@ -151,8 +161,16 @@ Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_readNative( FILL_ERROR("ReadFile() failed: %s (error code %ld)", buf); THROW_IO(NULL, buf); } + if (timeoutMillis > 0) { + DWORD res = WaitForSingleObject(olap.hEvent, timeoutMillis); + if (res == WAIT_TIMEOUT) { + CancelIoEx(handle, NULL); + char buf[256]; + FILL_ERROR("ReadFile() timed out: %s (error code %ld)", buf); + THROW_SOCKET_TIMEOUT(NULL, buf); + } + } } - if (!GetOverlappedResult(handle, &olap, &bytes_read, TRUE)) { char buf[256]; FILL_ERROR( diff --git a/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.h b/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.h index a30fbc8..67412b6 100644 --- a/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.h +++ b/jni/org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider.h @@ -42,10 +42,10 @@ JNIEXPORT jboolean JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryP /* * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider * Method: readNative - * Signature: (JJ[BIIZ)I + * Signature: (JJ[BIIZI)I */ JNIEXPORT jint JNICALL Java_org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider_readNative - (JNIEnv *, jobject, jlong, jlong, jbyteArray, jint, jint, jboolean); + (JNIEnv *, jobject, jlong, jlong, jbyteArray, jint, jint, jboolean, jint); /* * Class: org_scalasbt_ipcsocket_JNIWin32NamedPipeLibraryProvider diff --git a/src/main/java/org/scalasbt/ipcsocket/JNAWin32NamedPipeLibraryProvider.java b/src/main/java/org/scalasbt/ipcsocket/JNAWin32NamedPipeLibraryProvider.java index c5c7c85..c2ec3d2 100644 --- a/src/main/java/org/scalasbt/ipcsocket/JNAWin32NamedPipeLibraryProvider.java +++ b/src/main/java/org/scalasbt/ipcsocket/JNAWin32NamedPipeLibraryProvider.java @@ -1,6 +1,7 @@ package org.scalasbt.ipcsocket; import java.io.IOException; +import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import com.sun.jna.*; @@ -107,12 +108,10 @@ public boolean DisconnectNamedPipe(Handle handle) { public long PeekNamedPipe(Handle hFile) throws IOException { HANDLE handle = getHandle(hFile); IntByReference n = new IntByReference(); - boolean immediate = delegate.PeekNamedPipe(handle, null, 0, null, n, null); - if (!immediate) { + boolean ok = delegate.PeekNamedPipe(handle, null, 0, null, n, null); + if (!ok) { int lastError = delegate.GetLastError(); - if (lastError != WinError.ERROR_IO_PENDING) { - throw new IOException("ReadFile() failed: " + lastError); - } + throw new IOException("PeekNamedPipe() failed: " + lastError); } return n.getValue(); } @@ -124,7 +123,8 @@ public int read( byte[] buffer, int offset, int len, - boolean requireStrictLength) + boolean requireStrictLength, + int timeoutMillis) throws IOException { HANDLE readerWaitable = getHandle(waitable); HANDLE handle = getHandle(hFile); @@ -140,8 +140,13 @@ public int read( if (lastError != WinError.ERROR_IO_PENDING) { throw new IOException("ReadFile() failed: " + lastError); } + if (timeoutMillis > 0) { + int res = delegate.WaitForSingleObject(olap.hEvent, timeoutMillis); + if (res == WinError.WAIT_TIMEOUT) { + throw new SocketTimeoutException("ReadFile() timed out " + res); + } + } } - IntByReference r = new IntByReference(); if (!delegate.GetOverlappedResult(handle, olap.getPointer(), r, true)) { int lastError = delegate.GetLastError(); diff --git a/src/main/java/org/scalasbt/ipcsocket/JNIWin32NamedPipeLibraryProvider.java b/src/main/java/org/scalasbt/ipcsocket/JNIWin32NamedPipeLibraryProvider.java index b3b0467..8e3b69b 100644 --- a/src/main/java/org/scalasbt/ipcsocket/JNIWin32NamedPipeLibraryProvider.java +++ b/src/main/java/org/scalasbt/ipcsocket/JNIWin32NamedPipeLibraryProvider.java @@ -97,7 +97,8 @@ public int read( byte[] buffer, int offset, int len, - boolean requireStrictLength) + boolean requireStrictLength, + int timeoutMillis) throws IOException { return readNative( getHandlePointer(waitable), @@ -105,11 +106,18 @@ public int read( buffer, offset, len, - requireStrictLength); + requireStrictLength, + timeoutMillis); } native int readNative( - long waitable, long hFile, byte[] buffer, int offset, int len, boolean requireStrictLength) + long waitable, + long hFile, + byte[] buffer, + int offset, + int len, + boolean requireStrictLength, + int timeoutMillis) throws IOException; @Override diff --git a/src/main/java/org/scalasbt/ipcsocket/SocketChannels.java b/src/main/java/org/scalasbt/ipcsocket/SocketChannels.java index a72c24a..eefe738 100644 --- a/src/main/java/org/scalasbt/ipcsocket/SocketChannels.java +++ b/src/main/java/org/scalasbt/ipcsocket/SocketChannels.java @@ -83,7 +83,7 @@ public static String readLine(SocketChannel channel) throws IOException { } /** Utility function to read until newline from a non-blocking channel. */ - public static String readLine(SocketChannel channel, int readTimeoutMilis) throws IOException { + public static String readLine(SocketChannel channel, int readTimeoutMillis) throws IOException { int readBytes; byte b; final List values = new ArrayList<>(); @@ -96,19 +96,22 @@ public static String readLine(SocketChannel channel, int readTimeoutMilis) throw if (isJava17Plus() && !ServerSocketChannels.isWin) { if (!channel.isBlocking()) { channel.register(sel, SelectionKey.OP_READ); - numOfKeys = sel.select(readTimeoutMilis); + numOfKeys = sel.select(readTimeoutMillis); } } else { - if (readTimeoutMilis > 0) { - throw new IOException("timeout requires JDK 17 and non-Windows"); - } - if (channel.supportedOptions().contains(SO_TIMEOUT)) { - channel.setOption(SO_TIMEOUT, Integer.valueOf(readTimeoutMilis)); + if (readTimeoutMillis > 0) { + if (ServerSocketChannels.isWin) { + if (channel.supportedOptions().contains(SO_TIMEOUT)) { + channel.setOption(SO_TIMEOUT, Integer.valueOf(readTimeoutMillis)); + } + } else { + throw new IOException("timeout requires JDK 17 or Windows"); + } } } if (numOfKeys == 0) { throw new SocketTimeoutException( - "readLine timed out after " + Integer.toString(readTimeoutMilis) + " msec"); + "readLine timed out after " + Integer.toString(readTimeoutMillis) + " msec"); } else { readBytes = channel.read(buf); } @@ -133,7 +136,8 @@ public static ByteBuffer readAll(SocketChannel channel) throws IOException { } /** Utility function to read all buffer from a non-blocking channel. */ - public static ByteBuffer readAll(SocketChannel channel, int readTimeoutMilis) throws IOException { + public static ByteBuffer readAll(SocketChannel channel, int readTimeoutMillis) + throws IOException { int readBytes; final List values = new ArrayList<>(); final int bufSize = 1024 * 1024; @@ -145,22 +149,25 @@ public static ByteBuffer readAll(SocketChannel channel, int readTimeoutMilis) th if (isJava17Plus() && !ServerSocketChannels.isWin) { if (!channel.isBlocking()) { channel.register(sel, SelectionKey.OP_READ); - numOfKeys = sel.select(readTimeoutMilis); + numOfKeys = sel.select(readTimeoutMillis); } } else { - if (readTimeoutMilis > 0) { - throw new IOException("timeout requires JDK 17 and non-Windows"); - } - // The following operation gets blocked on JDK 8 - // channel.register(sel, SelectionKey.OP_READ); - // numOfKeys = sel.select(readTimeoutMilis); - if (channel.supportedOptions().contains(SO_TIMEOUT)) { - channel.setOption(SO_TIMEOUT, Integer.valueOf(readTimeoutMilis)); + if (readTimeoutMillis > 0) { + // The following operation gets blocked on JDK 8 + // channel.register(sel, SelectionKey.OP_READ); + // numOfKeys = sel.select(readTimeoutMillis); + if (ServerSocketChannels.isWin) { + if (channel.supportedOptions().contains(SO_TIMEOUT)) { + channel.setOption(SO_TIMEOUT, Integer.valueOf(readTimeoutMillis)); + } + } else { + throw new IOException("timeout requires JDK 17 or Windows"); + } } } if (numOfKeys == 0) { throw new SocketTimeoutException( - "readAll timed out after " + Integer.toString(readTimeoutMilis) + " msec"); + "readAll timed out after " + Integer.toString(readTimeoutMillis) + " msec"); } else { readBytes = channel.read(buf); } diff --git a/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibrary.java b/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibrary.java index 89f52dc..6ab1157 100644 --- a/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibrary.java +++ b/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibrary.java @@ -89,6 +89,8 @@ boolean GetOverlappedResult( boolean CancelIoEx(HANDLE hObject, Pointer lpOverlapped); + int WaitForSingleObject(HANDLE hObject, int dwMilliseconds); + HANDLE CreateEvent( SECURITY_ATTRIBUTES lpEventAttributes, boolean bManualReset, diff --git a/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibraryProvider.java b/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibraryProvider.java index 9c601f6..a32d175 100644 --- a/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibraryProvider.java +++ b/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeLibraryProvider.java @@ -27,7 +27,8 @@ int read( byte[] buffer, int offset, int len, - boolean requireStrictLength) + boolean requireStrictLength, + int timeoutMillis) throws IOException; void write(Handle waitable, Handle hFile, byte[] lpBuffer, int offset, int len) diff --git a/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeSocket.java b/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeSocket.java index 7e67bdc..6efa979 100644 --- a/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeSocket.java +++ b/src/main/java/org/scalasbt/ipcsocket/Win32NamedPipeSocket.java @@ -140,7 +140,8 @@ public int read() throws IOException { @Override public int read(byte[] b, int off, int len) throws IOException { - return provider.read(readerWaitable, handle, b, off, len, requireStrictLength); + return provider.read( + readerWaitable, handle, b, off, len, requireStrictLength, getSoTimeout()); } } diff --git a/src/main/resources/win32/x86_64/sbtipcsocket.dll b/src/main/resources/win32/x86_64/sbtipcsocket.dll index 1abd8b6724982c70008ffe222cc3eb56923ef44b..1e3926c8dc345fbd6c648cde5dc9d50029e43486 100755 GIT binary patch delta 23082 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 P_fN=UEfFVWC-Q#)kW0&1 diff --git a/src/test/java/org/scalasbt/ipcsocket/SocketChannelTest.java b/src/test/java/org/scalasbt/ipcsocket/SocketChannelTest.java index f5876d3..f96132e 100644 --- a/src/test/java/org/scalasbt/ipcsocket/SocketChannelTest.java +++ b/src/test/java/org/scalasbt/ipcsocket/SocketChannelTest.java @@ -21,7 +21,7 @@ public void testNonBlockingEchoServer() throws IOException, InterruptedException "SocketChannelTest#testNonBlockingEchoServer(" + Boolean.toString(useJNI()) + ")"); withSocket( sock -> { - if (isJava17Plus() && !ServerSocketChannels.isWin) { + if (isJava17Plus() || ServerSocketChannels.isWin) { String line = nonBlockingEchoServerTest(sock, 100, 600); assertEquals("echo did not return the content", "hello", line); } @@ -34,7 +34,7 @@ public void testTimeout() throws IOException, InterruptedException { System.out.println("SocketChannelTest#testTimeout(" + Boolean.toString(useJNI()) + ")"); withSocket( sock -> { - if (isJava17Plus() && !ServerSocketChannels.isWin) { + if (isJava17Plus() || ServerSocketChannels.isWin) { String line = nonBlockingEchoServerTest(sock, 6000, 600); assertEquals("echo did not timeout", "", line); }