Brings Sourceforge's current no2 most-often downloaded p2p app to life.

Well, this code is a straight adaption of GCJ's CNI equivalent. However two bug reports have been created out of this.

Please review the patch and besides normal critique answer me the following questions:

1) Should we introduce a HAVE_SELECT switch as GCJ has?
(If yes I need help with this.)

2) strerror() is not thread-safe. Should I prefer strerror_r() which is less portable?


The changelog entry:

2005-03-17  Robert Schuster  <[EMAIL PROTECTED]>

   * native/jni/java-nio/gnu_java_nio_VMSelector.c: Implemented
   Java_gnu_java_nio_VMSelector_select.

cu
Robert
Index: native/jni/java-nio/gnu_java_nio_VMSelector.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/java-nio/gnu_java_nio_VMSelector.c,v
retrieving revision 1.2
diff -u -r1.2 gnu_java_nio_VMSelector.c
--- native/jni/java-nio/gnu_java_nio_VMSelector.c	26 Oct 2004 20:26:03 -0000	1.2
+++ native/jni/java-nio/gnu_java_nio_VMSelector.c	17 Mar 2005 05:37:26 -0000
@@ -1,5 +1,5 @@
 /* gnu_java_nio_VMSelector.c - Native methods for SelectorImpl class
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -35,29 +35,237 @@
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
+#include <sys/select.h>
+#include <sys/time.h>
+
+#include <string.h>
+
 #include <config.h>
 #include <errno.h>
 
 #include <jni.h>
 #include <jcl.h>
 
+#include <stdio.h>
+
 #include "gnu_java_nio_VMSelector.h"
 
-#define IO_EXCEPTION "java/io/IOException"
+void
+helper_put_filedescriptors(JNIEnv *, jintArray, fd_set *, int *);
+
+void
+helper_get_filedescriptors (JNIEnv *, jintArray*, fd_set *);
+
+void
+helper_reset (JNIEnv *, jintArray*);
+
+int
+helper_select (JNIEnv *, jclass, jmethodID,
+		int, fd_set *, fd_set  *, fd_set *,
+		struct timeval *);
+
+void
+helper_put_filedescriptors(JNIEnv *env, jintArray fdArray, fd_set *fds, int *max_fd)
+{
+	jint *tmpFDArray = (*env)->GetIntArrayElements(env, fdArray, 0);
+	int size = (*env)->GetArrayLength(env, fdArray);
+	int index, fd;
+	
+	for( index = 0; index < size; index++)
+	{
+		fd = tmpFDArray [index];
+      	
+      	if (fd > 0)
+        {
+          FD_SET (tmpFDArray [index], fds);
+
+          if (tmpFDArray [index] > (*max_fd))
+            (*max_fd) = tmpFDArray [index];
+        }
+    }
+}
+
+void
+helper_get_filedescriptors (JNIEnv *env, jintArray* fdArray, fd_set *fds)
+{
+	jint *tmpFDArray = (*env)->GetIntArrayElements(env, fdArray, 0);
+	int size = (*env)->GetArrayLength(env, fdArray);
+	int index, fd;
+
+	for (index = 0; index < size; index++)
+    {
+      fd = tmpFDArray [index];
+      if (fd < 0 || !FD_ISSET (fd, fds))
+        tmpFDArray [index] = 0;
+    }
+}
+
+void
+helper_reset (JNIEnv *env, jintArray* fdArray)
+{
+	jint* tmpFDArray = (*env)->GetIntArrayElements(env, fdArray, 0);
+	int size = (*env)->GetArrayLength(env, fdArray);
+	int index;
+
+	for (index = 0; index < size; index++)
+		tmpFDArray [index] = 0;
+}
+
+/* A wrapper for select() which ignores EINTR.
+ * Taken from gclib's posix.cc
+ */
+int
+helper_select (JNIEnv *env, jclass thread_class, jmethodID thread_interrupted,
+		int n, fd_set *readfds, fd_set  *writefds, fd_set *exceptfds,
+		struct timeval *timeout)
+{
+/* FIXME: Should we support this, too? Or better fail?
+#ifdef HAVE_SELECT */
+	/* If we have a timeout, compute the absolute ending time. */
+	struct timeval end, delay, after;
+	int r;
+  
+	if (timeout)
+	{
+		gettimeofday (&end, NULL);
+		
+		end.tv_usec += timeout->tv_usec;
+		
+		if (end.tv_usec >= 1000000)
+		{
+			++end.tv_sec;
+			end.tv_usec -= 1000000;
+		}
+		
+		end.tv_sec += timeout->tv_sec;
+		delay = *timeout;
+	}
+	else
+	{
+		/* Placate compiler. */
+		delay.tv_sec = delay.tv_usec = 0;
+	}
+
+	while (1)
+	{
+		r = select (n, readfds, writefds, exceptfds,
+		      timeout ? &delay : NULL);
+		      
+		if (r != -1 || errno != EINTR)
+			return r;
+
+		/* Here we know we got EINTR. */
+		if ( (*env)->CallStaticBooleanMethod(env, thread_class, thread_interrupted) ) {
+			return EINTR;
+		}
+
+		if (timeout)
+		{
+			gettimeofday (&after, NULL);
+			
+			/* Now compute new timeout argument. */
+			delay.tv_usec = end.tv_usec - after.tv_usec;
+			delay.tv_sec = end.tv_sec - after.tv_sec;
+			
+			if (delay.tv_usec < 0)
+			{
+				--delay.tv_sec;
+				delay.tv_usec += 1000000;
+			}
+
+			if (delay.tv_sec < 0)
+			{
+				/* We assume that the user wants a valid select() call
+				 * more than precise timing.  So if we get a series of
+				 * EINTR we just keep trying with delay 0 until we get a
+				 * valid result.
+				 */
+				delay.tv_sec = 0;
+			}
+		}
+	}
+/*
+#else HAVE_SELECT
+  return 0;
+#endif
+*/
+}
 
 JNIEXPORT jint JNICALL
 Java_gnu_java_nio_VMSelector_select (JNIEnv *env,
 				     jclass obj __attribute__ ((__unused__)),
-				     jintArray read
-				     __attribute__ ((__unused__)),
-				     jintArray write
-				     __attribute__ ((__unused__)),
-				     jintArray except
-				     __attribute__ ((__unused__)),
-				     jlong timeout
-				     __attribute__ ((__unused__)))
+				     jintArray read,
+				     jintArray write,
+				     jintArray except,
+				     jlong timeout)
 {
-  JCL_ThrowException (env, IO_EXCEPTION, "gnu.java.nio.VMSelector.select(): not implemented");
-  return 0;
+	jint result;
+	jclass thread_class = (*env)->FindClass(env, "java/lang/Thread");
+	jmethodID thread_current_thread = (*env)->GetStaticMethodID(env, thread_class, "currentThread", "()Ljava/lang/Thread;");
+	jmethodID thread_interrupt = (*env)->GetMethodID(env, thread_class, "interrupt", "()V");
+	jmethodID thread_interrupted = (*env)->GetMethodID(env, thread_class, "interrupted", "()Z");
+	jobject current_thread;
+	int max_fd = 0;
+	fd_set read_fds;
+	fd_set write_fds;
+	fd_set except_fds;
+	struct timeval real_time_data;
+	struct timeval *time_data = NULL;
+	
+	/* If a legal timeout value isn't given, use NULL.
+	 * This means an infinite timeout. The specification
+	 * also says that a zero timeout should be treated
+	 * as infinite. Otherwise (if the timeout value is legal),
+	 * fill our timeval struct and use it for the select.
+	 */
+	if (timeout > 0)
+	{
+		real_time_data.tv_sec = timeout / 1000;
+		real_time_data.tv_usec = (timeout % 1000) * 1000;
+		time_data = &real_time_data;
+	}
+
+	/* Reset all fd_set structures */
+	FD_ZERO (&read_fds);
+	FD_ZERO (&write_fds);
+	FD_ZERO (&except_fds);
+
+	/* Fill the fd_set data structures for the _Jv_select() call. */
+	helper_put_filedescriptors (env, read, &read_fds, &max_fd);
+	helper_put_filedescriptors (env, write, &write_fds, &max_fd);
+	helper_put_filedescriptors (env, except, &except_fds, &max_fd);
+
+	/* Actually do the select */
+	result = helper_select (env, thread_class, thread_interrupted, max_fd + 1, &read_fds, &write_fds,
+								&except_fds, time_data);
+
+	if( result == EINTR ) {
+		/* The behavior of JRE 1.4.1 is that no exception is thrown
+		 * when the thread is interrupted, but the thread's interrupt
+		 * status is set. Clear all of our select sets and return 0,
+		 * indicating that nothing was selected.
+		 */
+		current_thread = (*env)->CallStaticObjectMethod(env, thread_class, thread_current_thread);
+		(*env)->CallVoidMethod(env, current_thread, thread_interrupt);
+      
+		helper_reset (env, read);
+		helper_reset (env, write);
+		helper_reset (env, except);
+		
+		return 0;
+	}
+	
+	if (result < 0)
+    {
+      JCL_ThrowException (env, "java/io/IOException", strerror (errno));
+      return 0;
+    }
+
+	/* Set the file descriptors according to the values returned from select(). */
+	helper_get_filedescriptors (env, read, &read_fds);
+	helper_get_filedescriptors (env, write, &write_fds);
+	helper_get_filedescriptors (env, except, &except_fds);
+
+	return result;
 }
 
_______________________________________________
Classpath-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/classpath-patches

Reply via email to