From 5c51df50a51db38dd16dd461c9b961bc2e72ddba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Christian=20Kohlschu=CC=88tter?=
+ * If the given blocking mode is different from the currently cached blocking mode then this
+ * method native code to change it.
+ *
+ * Also note that the "blocking" state of a socket may be forcibly changed to "blocking" when + * performing the cast, especially when casting to {@link Socket}, {@link DatagramSocket} or + * {@link ServerSocket} and any of their subclasses where "blocking" is the expected state. + *
* * @author Christian Kohlschütter */ @@ -225,21 +235,24 @@ static void registerCastingProviders( protected void addProviders() { addProviders(GLOBAL_PROVIDERS); - final CastingProvider+ * If {@code isChannel} is false, then we want to cast to a {@link Socket}, {@link DatagramSocket} + * or {@link ServerSocket}, which means blocking I/O is desired. If the underlying native socket + * is configured non-blocking, we need to reset the state to "blocking" accordingly. + *
+ * If {@code isChannel} is true, then we want to cast to a {@link SocketChannel}, + * {@link DatagramChannel} or {@link ServerSocketChannel}, in which case the blocking state should + * be preserved, if possible. It is then up to the user to check blocking state via + * {@link AbstractSelectableChannel#isBlocking()} prior to using the socket. + *
+ * Note that on Windows, it may be impossible to query the blocking state from an external socket,
+ * so the state is always forcibly set to "blocking".
+ *
+ * @param The type.
+ * @param isChannel The desired cast type (socket=set to blocking, or channel=preserve state).
+ * @param socketChannel The channel.
+ * @throws IOException on error.
+ */
+ private static <@NonNull S extends AFSomeSocketChannel> void reconfigure(boolean isChannel,
+ S socketChannel) throws IOException {
+ if (isChannel) {
+ reconfigureKeepBlockingState(socketChannel);
+ } else {
+ reconfigureSetBlocking(socketChannel);
+ }
+ }
+
+ private static <@NonNull S extends AFSomeSocketChannel> void reconfigureKeepBlockingState(
+ S socketChannel) throws IOException {
+ int result = NativeUnixSocket.checkBlocking(socketChannel.getFileDescriptor());
+
+ boolean blocking;
+ switch (result) {
+ case 0:
+ blocking = false;
+ break;
+ case 1:
+ blocking = true;
+ break;
+ case 2:
+ // need to reconfigure/forcibly override any cached result -> set to blocking by default
+ socketChannel.configureBlocking(false);
+ socketChannel.configureBlocking(true);
+ return;
+ default:
+ throw new OperationNotSupportedSocketException("Invalid blocking state");
+ }
+
+ socketChannel.configureBlocking(blocking);
+ }
+
+ private static <@NonNull S extends AFSomeSocketChannel> void reconfigureSetBlocking(
+ S socketChannel) throws IOException {
+ int result = NativeUnixSocket.checkBlocking(socketChannel.getFileDescriptor());
+
+ switch (result) {
+ case 0:
+ // see below
+ break;
+ case 1:
+ // already blocking, nothing to do
+ return;
+ case 2:
+ // need to reconfigure/forcibly override any cached result -> set to blocking by default
+ // see below
+ break;
+ default:
+ throw new OperationNotSupportedSocketException("Invalid blocking state");
+ }
+
+ socketChannel.configureBlocking(false);
+ socketChannel.configureBlocking(true);
+ }
}
diff --git a/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java b/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java
index c17e73834..0fc427997 100644
--- a/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java
+++ b/junixsocket-common/src/main/java/org/newsclub/net/unix/NativeUnixSocket.java
@@ -298,6 +298,15 @@ static native boolean initPipe(FileDescriptor source, FileDescriptor sink, boole
static native void configureBlocking(FileDescriptor fd, boolean blocking) throws IOException;
+ /**
+ * Checks if the given file descriptor describes a blocking socket.
+ *
+ * @param fd The file descriptor to check
+ * @return 0 = non-blocking, 1 = blocking, 2 = indeterminate (needs reconfiguration)
+ * @throws IOException on error.
+ */
+ static native int checkBlocking(FileDescriptor fd) throws IOException;
+
static native void socketPair(int domain, int type, FileDescriptor fd, FileDescriptor fd2);
static native Redirect initRedirect(FileDescriptor fd);
diff --git a/junixsocket-native/src/main/c/filedescriptors.c b/junixsocket-native/src/main/c/filedescriptors.c
index 0e3df008d..0ecfca2f9 100644
--- a/junixsocket-native/src/main/c/filedescriptors.c
+++ b/junixsocket-native/src/main/c/filedescriptors.c
@@ -338,6 +338,33 @@ JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_shutdown
}
}
+/*
+ * Class: org_newsclub_net_unix_NativeUnixSocket
+ * Method: checkBlocking
+ * Signature: (Ljava/io/FileDescriptor;)I
+ */
+JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_checkBlocking
+ (JNIEnv *env, jclass clazz CK_UNUSED, jobject fd) {
+ int handle = _getFD(env, fd);
+#if defined(_WIN32)
+ CK_ARGUMENT_POTENTIALLY_UNUSED(handle);
+ // Windows doesn't provide current API to check for blocking state
+ return 2; // "indeterminate; needs re-configure"
+#else
+ int flags = fcntl(handle, F_GETFL);
+ if(flags == -1) {
+ _throwErrnumException(env, socket_errno, NULL);
+ return -1;
+ }
+
+ if((flags & O_NONBLOCK) != 0) {
+ return 0; // "non-blocking"
+ } else {
+ return 1; // "blocking"
+ }
+#endif
+}
+
/*
* Class: org_newsclub_net_unix_NativeUnixSocket
* Method: configureBlocking
diff --git a/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h b/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h
index 507ae4ae8..54fcf19d5 100644
--- a/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h
+++ b/junixsocket-native/src/main/c/org_newsclub_net_unix_NativeUnixSocket.h
@@ -381,6 +381,14 @@ JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_poll
JNIEXPORT void JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_configureBlocking
(JNIEnv *, jclass, jobject, jboolean);
+/*
+ * Class: org_newsclub_net_unix_NativeUnixSocket
+ * Method: checkBlocking
+ * Signature: (Ljava/io/FileDescriptor;)I
+ */
+JNIEXPORT jint JNICALL Java_org_newsclub_net_unix_NativeUnixSocket_checkBlocking
+ (JNIEnv *, jclass, jobject);
+
/*
* Class: org_newsclub_net_unix_NativeUnixSocket
* Method: socketPair