Hi,

Here is a patch to correct the handling of socket timeouts in the
NATIVE-LAYER branch. This patch relies on the select function (which is
not the most portable way). A few syscalls should also be protected that
way but they will come in a forthcoming patch (like connect).

ChangeLog entry:
2006-05-10  Guilhem Lavaux  <[EMAIL PROTECTED]>

        * native/jni/native-lib/cpnet.c
        (cpnet_getSocketTimeout, cpnet_setSocketTimeout): Reimplemented.
        (waitForWritable, waitForReadable): New functions.
        (socketTimeouts): New static global table to hold timeouts for all
        socket fds.
        (cpnet_accept,cpnet_bind,cpnet_sendTo,cpnet_recv,cpnet_recvFrom):
        Added waitForXXXX safeguards to handle socket timeouts.

        * native/jni/java-net/javanet.c
        (_javanet_accept): Check for the right error value when a timeout
        occurs.


Index: native/jni/java-net/javanet.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/java-net/javanet.c,v
retrieving revision 1.32.2.7
diff -u -r1.32.2.7 javanet.c
--- native/jni/java-net/javanet.c       19 Feb 2006 17:10:27 -0000      1.32.2.7
+++ native/jni/java-net/javanet.c       10 May 2006 07:04:35 -0000
@@ -795,7 +795,7 @@
       result = cpnet_accept (env, fd, &newfd);
       if (result != CPNATIVE_OK && result != CPNATIVE_EINTR)
        {
-         if (result == EAGAIN)
+         if (result == ETIMEDOUT)
            JCL_ThrowException (env, "java/net/SocketTimeoutException",
                                "Timeout");
          else
Index: native/jni/native-lib/cpnet.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/native-lib/Attic/cpnet.c,v
retrieving revision 1.1.2.3
diff -u -r1.1.2.3 cpnet.c
--- native/jni/native-lib/cpnet.c       25 Mar 2006 17:08:50 -0000      1.1.2.3
+++ native/jni/native-lib/cpnet.c       10 May 2006 07:04:35 -0000
@@ -37,6 +37,7 @@
 
 #include "config.h"
 #include <jni.h>
+#include <assert.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -46,9 +47,57 @@
 #include <netinet/tcp.h>
 #include <netdb.h>
 #include <sys/ioctl.h>
+#include <sys/time.h>
+#include <unistd.h>
 
 #include "cpnet.h"
 
+#define SOCKET_DEFAULT_TIMEOUT -1 /* milliseconds */
+
+static int socketTimeouts[FD_SETSIZE];
+
+static jint waitForWritable(jint fd)
+{
+  struct timeval tv;
+  fd_set writeset;
+  int ret;
+  
+ 
+  FD_ZERO(&writeset);
+  FD_SET(fd, &writeset);
+  if (socketTimeouts[fd] > 0)
+    {
+      tv.tv_sec = socketTimeouts[fd] / 1000;
+      tv.tv_usec = (socketTimeouts[fd] % 1000) * 1000;
+      ret = select(fd+1, NULL, &writeset, NULL, &tv);
+    }
+  else
+    ret = select(fd+1, NULL, &writeset, NULL, NULL);
+
+  return (ret <= 0) ? -1 : 0;
+}
+
+static jint waitForReadable(jint fd)
+{
+  struct timeval tv;
+  fd_set readset;
+  int ret;
+
+
+  FD_ZERO(&readset);
+  FD_SET(fd, &readset);
+  if (socketTimeouts[fd] > 0)
+    {
+      tv.tv_sec = socketTimeouts[fd] / 1000;
+      tv.tv_usec = (socketTimeouts[fd] % 1000) * 1000;
+      ret = select(fd+1, &readset, NULL, NULL, &tv);
+    }
+  else
+    ret = select(fd+1, &readset, NULL, NULL, NULL);
+
+  return (ret <= 0) ? -1 : 0;
+}
+
 jint cpnet_openSocketStream(JNIEnv *env UNUSED, jint *fd, jint family)
 {
   *fd = socket(family, SOCK_STREAM, 0);
@@ -56,6 +105,8 @@
     return errno;
 
   fcntl(*fd, F_SETFD, FD_CLOEXEC);
+  assert(*fd < FD_SETSIZE);
+  socketTimeouts[*fd] = SOCKET_DEFAULT_TIMEOUT;
   return 0;
 }
 
@@ -66,6 +117,8 @@
     return errno;
 
   fcntl(*fd, F_SETFD, FD_CLOEXEC);
+  assert(*fd < FD_SETSIZE);
+  socketTimeouts[*fd] = SOCKET_DEFAULT_TIMEOUT;
   return 0;
 }
 
@@ -101,6 +154,9 @@
 
 jint cpnet_accept(JNIEnv *env UNUSED, jint fd, jint *newfd)
 {
+  if (waitForReadable (fd) < 0)
+    return ETIMEDOUT;
+
   *newfd = accept(fd, NULL, 0);
   if (*newfd != 0)
     return errno;
@@ -123,6 +179,7 @@
 {
   int ret;
 
+  /* TODO: implement socket time out */
   ret = connect(fd, (struct sockaddr *)addr->data, addr->len);
   if (ret != 0)
     return errno;
@@ -187,6 +244,9 @@
 {
   ssize_t ret;
 
+  if (waitForWritable(fd) < 0)
+    return ETIMEDOUT;
+
   ret = send(fd, data, len, MSG_NOSIGNAL);
   if (ret < 0)
     return errno;
@@ -200,6 +260,9 @@
 {
   ssize_t ret;
 
+  if (waitForWritable(fd) < 0)
+    return ETIMEDOUT;
+
   ret = sendto(fd, data, len, MSG_NOSIGNAL, (struct sockaddr *)addr->data, 
addr->len);
   if (ret < 0)
     return errno;
@@ -212,6 +275,9 @@
 {
   ssize_t ret;
 
+  if (waitForReadable(fd) < 0)
+    return ETIMEDOUT;
+
   ret = recv(fd, data, len, 0);
   if (ret < 0)
     return errno;
@@ -226,6 +292,9 @@
   socklen_t slen = 1024;
   ssize_t ret;
 
+  if (waitForReadable(fd) < 0)
+    return ETIMEDOUT;
+
   *addr = JCL_malloc(env, slen);
 
   slen -= sizeof(jint);
@@ -238,7 +307,7 @@
     }
 
   (*addr)->len = slen;
-  *bytes_recv = len;
+  *bytes_recv = ret;
 
   return 0;
 }
@@ -306,46 +375,16 @@
   return ret;
 }
 
-jint cpnet_setSocketTimeout (JNIEnv *env UNUSED, jint fd UNUSED, jint value 
UNUSED)
+jint cpnet_setSocketTimeout (JNIEnv *env UNUSED, jint fd, jint value)
 {
-#if 0
-  /* This is not really the right default implementation. This will have to
-   * be changed. Most OSes do not completely/rightfully support SO_TIMEOUT.
-   */
-  struct timeval tval;
-  int ret;
-
-  tval.tv_sec = value / 1000;
-  tval.tv_usec = (value % 1000) * 1000;
-  ret = setsockopt (fd, SOL_SOCKET, SO_TIMEOUT, &tval, sizeof(tval));
-  if (ret != 0)
-    return errno;
-
+  socketTimeouts[fd] = value;
   return 0;
-#else
-  return ENOSYS;
-#endif
 }
 
-jint cpnet_getSocketTimeout (JNIEnv *env UNUSED, jint fd UNUSED, jint *value 
UNUSED)
+jint cpnet_getSocketTimeout (JNIEnv *env UNUSED, jint fd, jint *value)
 {
-#if 0
-  /* This is not really the right default implementation. This will have to
-   * be changed. Most OSes do not completely/rightfully support SO_TIMEOUT.
-   */
-  struct timeval tval;
-  int ret;
-
-  ret = getsockopt (fd, SOL_SOCKET, SO_TIMEOUT, &tval, sizeof(tval));
-  if (ret != 0)
-    return errno;
-
-  *value = (tval.tv_sec * 1000) + (tval.tv_usec / 1000);
-
+  *value = socketTimeouts[fd];
   return 0;
-#else
-  return ENOSYS;
-#endif
 }
 
 jint cpnet_setSendBuf (JNIEnv *env UNUSED, jint fd, jint value)

Reply via email to