This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

PlainSocketImpl changes to support socket timeouts


I've changed the patch for socket timeouts.

This one immitates the behaviour of Sun's JDK much better.


Nic Ferrier
2001-07-05  Nic Ferrier  <nferrier@tf1.tapsellferrier.co.uk>

	* natPlainSocketImpl.cc: added timeout handling for sockets.
	(close): new function closes the socket
	(write): new functions for output to socket
	(read): new functions for reading from socket
	* PlainSocketImpl.java: glue for new timeout implementation
	(write): calls the native impl
	(read): likewise
	(getInputStream): gets a stream to read from the socket
	(getOutputStream): gets a stream to write to the socket
Index: libjava/java/net/PlainSocketImpl.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/net/PlainSocketImpl.java,v
retrieving revision 1.8
diff -u -r1.8 PlainSocketImpl.java
--- PlainSocketImpl.java	2000/12/08 10:28:32	1.8
+++ PlainSocketImpl.java	2001/07/05 13:31:41
@@ -1,6 +1,6 @@
 // PlainSocketImpl.java - Implementation of SocketImpl.
 
-/* Copyright (C) 1999  Free Software Foundation
+/* Copyright (C) 1999,2001  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -11,17 +11,19 @@
 package java.net;
 import java.io.*;
 
-/**
- * @author Per Bothner <bothner@cygnus.com>
- * @date February 22, 1999.
- */
-
-/**
- * Written using on-line Java Platform 1.2 API Specification, as well
+/** the standard socket implementation.
+ * This is the implementation returned by default when sockets
+ * are created.
+ *
+ * <h4>Design notes</h4>
+ * <p>Written using on-line Java Platform 1.2 API Specification, as well
  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
- * Status:  Believed complete and correct.
+ * </p>
+ *
+ * @author Per Bothner <bothner@cygnus.com>
+ * @author Nic Ferrier <nferrier@tapsellferrier.co.uk>
+ * @date June 2001
  */
-
 class PlainSocketImpl extends SocketImpl
 {
   // These fields are mirrored for use in native code to avoid cpp conflicts
@@ -35,6 +37,18 @@
                    _Jv_SO_SNDBUF_ = SocketOptions.SO_SNDBUF,
                    _Jv_SO_RCVBUF_ = SocketOptions.SO_RCVBUF;
 
+  /** the socket's file handle.
+   * This is the socket handle that is returned from the creation
+   * of the socket. It is used for read and writes to the socket and
+   * to close the socket.
+   *
+   * {@link SocketImpl#fd} is created from this like so:
+   * <pre>
+   *   fd=new FileDescriptor(fnum);
+   * </pre>
+   *
+   * When the socket is closed this is reset to -1.
+   */
   int fnum = -1;
 
   // This value is set/read by setOption/getOption.
@@ -62,37 +76,131 @@
   protected native void listen (int backlog) throws IOException;
 
   private native void accept (PlainSocketImpl s) throws IOException;
+
   protected void accept (SocketImpl s) throws IOException
   {
     accept((PlainSocketImpl) s);
   }
+
+  protected synchronized native void close () throws IOException;
+
+
+  //the native read methods
+
+  private native int read() throws IOException;
+
+  private native int read(byte[] buffer,int offset,int count) throws IOException;
+
+  protected native int available() throws IOException;
+
 
-  private InputStream in;
-  private OutputStream out;
+  //the native write methods
 
+  private native void write(int c) throws IOException;
+
+  private native void write(byte[] buffer,int offset,int count) throws IOException;
+
+
+  //stream handling
+
+  /** a cached copy of the in stream.
+   */
+  private InputStream in=null;
+
+  /** a cached copy of the out stream.
+   */
+  private OutputStream out=null;
+
   protected InputStream getInputStream() throws IOException
   {
-    // FIXME: TODO - Implement class SocketInputStream timeouts in read();
-    if (in == null)
-      in = new FileInputStream (fd);
+    if(in==null)
+      in=new SocketInputStream(this);
     return in;
   }
 
   protected OutputStream getOutputStream() throws IOException
   {
     if (out == null)
-      out = new FileOutputStream (fd);
+      out = new SocketOutputStream (this);
     return out;
   }
 
-  protected int available () throws IOException
-  {
-    return in.available();
+
+  /** input stream which reads from the socket implementation.
+   *
+   * @author Nic Ferrier, nferrier@tapsellferrier.co.uk
+   */
+  class SocketInputStream
+    extends InputStream
+  {
+    PlainSocketImpl socket;
+    
+    SocketInputStream(PlainSocketImpl socket)
+    {
+      this.socket=socket;
+    }
+
+    public void close() throws IOException
+    {
+      socket.close();
+    }
+
+    public int available() throws IOException
+    {
+      return socket.available();
+    }
+
+    public int read() throws IOException
+    {
+      return socket.read();
+    }
+
+    public int read(byte[] buffer,int offset,int length) throws IOException
+    {
+      return socket.read(buffer,offset,length);
+    }
+
+    public int read(byte[] buffer) throws IOException
+    {
+      return read(buffer,0,buffer.length);
+    }
   }
 
-  protected void close () throws IOException
-  {
-    if (fd.valid())
-      fd.close();
+  
+  /** output stream which writes to the socket implementation.
+   *
+   * @author Nic Ferrier, nferrier@tapsellferrier.co.uk
+   */
+  class SocketOutputStream
+    extends OutputStream
+  {
+    PlainSocketImpl socket;
+    
+    SocketOutputStream(PlainSocketImpl socket)
+    {
+      this.socket=socket;
+    }
+
+    public void close() throws IOException
+    {
+      socket.close();
+    }
+
+    public void write(int c) throws IOException
+    {
+      socket.write(c);
+    }
+
+    public void write(byte[] buffer,int offset,int length) throws IOException
+    {
+      socket.write(buffer,offset,length);
+    }
+
+    public void write(byte[] buffer) throws IOException
+    {
+      write(buffer,0,buffer.length);
+    }
   }
+  
 }
+
Index: libjava/java/net/natPlainSocketImpl.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/net/natPlainSocketImpl.cc,v
retrieving revision 1.23.4.1
diff -u -r1.23.4.1 natPlainSocketImpl.cc
--- natPlainSocketImpl.cc	2001/05/24 03:07:51	1.23.4.1
+++ natPlainSocketImpl.cc	2001/07/05 13:31:42
@@ -8,7 +8,6 @@
 
 #include <config.h>
 
-
 #ifndef DISABLE_JAVA_NET
 #ifdef USE_WINSOCK
 #include <windows.h>
@@ -70,6 +69,9 @@
 #include <java/lang/Boolean.h>
 #include <java/lang/Class.h>
 #include <java/lang/Integer.h>
+#include <java/lang/Thread.h>
+#include <java/lang/NullPointerException.h>
+#include <java/lang/ArrayIndexOutOfBoundsException.h>
 
 #define BooleanClass java::lang::Boolean::class$
 
@@ -111,6 +113,41 @@
 }
 
 void
+java::net::PlainSocketImpl::close ()
+{
+  throw new java::io::IOException( JvNewStringLatin1 ("SocketImpl.close unimplemnted") );
+}
+
+void
+java::net::PlainSocketImpl::write( jint c)
+{
+  //this can never happen - you can't call write on something that hasn't been created
+  throw new java::io::IOException ( JvNewStringLatin1 ("Socket writes unimplemented") );
+}
+
+void
+java::net::PlainSocketImpl::write(jbyteArray b, jint offset, jint len)
+{
+  //this can never happen - you can't call write on something that hasn't been created
+  throw new java::io::IOException ( JvNewStringLatin1 ("Socket writes unimplemented") );
+}
+
+jint
+java::net::PlainSocketImpl::read( )
+{
+  //this can never happen - you can't call write on something that hasn't been created
+  throw new java::io::IOException ( JvNewStringLatin1 ("Socket reads unimplemented") );
+}
+
+jint
+java::net::PlainSocketImpl::read(jbyteArray b, jint offset, jint len)
+{
+  //this can never happen - you can't call write on something that hasn't been created
+  throw new java::io::IOException ( JvNewStringLatin1 ("Socket reads unimplemented") );
+}
+
+
+void
 java::net::PlainSocketImpl::setOption (jint, java::lang::Object *)
 {
   throw new SocketException (
@@ -315,6 +352,293 @@
   char* strerr = strerror (errno);
   throw new java::io::IOException (JvNewStringUTF (strerr));
 }
+
+//close (shutdown) the socket
+void
+java::net::PlainSocketImpl::close ()
+{
+  //synchronize this method
+  JvSynchronize sync (this);
+
+  if (fnum != -1 )
+    {
+      //reset the socket fd
+      fnum = -1;
+
+      int res = ::shutdown(fnum, 2);
+      if (res == -1 && (errno == ENOTCONN 
+			|| errno == ENETDOWN 
+			|| errno == ENETUNREACH 
+			|| errno == ENETRESET
+			|| errno == ECONNABORTED
+			|| errno == ECONNRESET 
+			|| errno == ETIMEDOUT))
+	throw new java::io::IOException( JvNewStringUTF (strerror (errno)));
+    }
+
+  //end synch
+}
+
+/*
+  we don't use non-blocking mode in any of these write methods... 
+  presumably the socket is open already so won't block.
+  There's a potential issue here though - would it be a good idea to have
+  a socket IO thread that handled writes? 
+  We need some tests to find out how much write blocking occurs.
+
+  Nic Ferrier - June 2001
+*/
+
+//write a byte to the socket
+void
+java::net::PlainSocketImpl::write (jint b)
+{
+  jbyte d = (jbyte) b;
+  int r = ::write (fnum, &d, 1);
+  if (java::lang::Thread::interrupted())
+    {
+      java::io::InterruptedIOException *iioe
+	= new java::io::InterruptedIOException (JvNewStringUTF ("write interrupted"));
+      iioe->bytesTransferred = r == -1 ? 0 : r;
+      throw iioe;
+    }
+  else if (r == -1)
+    {
+      //some errors cause us to return end of stream
+      if (errno == ENOTCONN 
+	  || errno == ENETDOWN 
+	  || errno == ENETUNREACH 
+	  || errno == ENETRESET
+	  || errno == ECONNABORTED
+	  || errno == ECONNRESET 
+	  || errno == ETIMEDOUT)
+	{
+	  fnum = -1;
+	}
+      else
+	throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+    }
+  // FIXME: loop if r != 1.
+}
+
+//write some bytes to the socket
+void
+java::net::PlainSocketImpl::write (jbyteArray b, jint offset, jint len)
+{
+  if (! b)
+    throw new java::lang::NullPointerException;
+  if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
+    throw new java::lang::ArrayIndexOutOfBoundsException;
+  jbyte *bytes = elements (b) + offset;
+  int r = ::write (fnum, bytes, len);
+  if (java::lang::Thread::interrupted())
+    {
+      java::io::InterruptedIOException *iioe
+	= new java::io::InterruptedIOException (JvNewStringUTF ("write interrupted"));
+      iioe->bytesTransferred = r == -1 ? 0 : r;
+      throw iioe;
+    }
+  else if (r == -1)
+    {
+      //some errors cause us to signal end of stream
+      if (errno == ENOTCONN 
+	  || errno == ENETDOWN 
+	  || errno == ENETUNREACH 
+	  || errno == ENETRESET
+	  || errno == ECONNABORTED
+	  || errno == ECONNRESET 
+	  || errno == ETIMEDOUT)
+	{
+	  fnum = -1;
+	}
+      else
+	throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+    }
+  // FIXME: loop if r != len.
+}
+
+
+//read a single byte from the socket
+jint
+java::net::PlainSocketImpl::read (void)
+{
+  jbyte b;
+
+  //do timeouts via select
+  if (timeout > 0)
+  {
+    //create the file descriptor set
+    fd_set read_fds;
+    FD_ZERO( &read_fds);
+    FD_SET(fnum, &read_fds);
+    //create the timeout struct based on our internal timeout value
+    struct timeval timeout_value;
+    timeout_value.tv_sec = timeout/1000;
+    timeout_value.tv_usec = (timeout%1000)*1000;
+    //select on the fds
+    int sel_retval = _Jv_select(fnum+1, &read_fds, NULL, NULL, &timeout_value);
+    if (sel_retval == 0)
+      throw new java::io::InterruptedIOException(JvNewStringUTF("read timed out") );
+    //if select returns we know we either got signalled or read some data...
+    //either way we need to try to read.
+  }
+  int r = ::read (fnum, &b, 1);
+
+  if (r == 0)
+    return -1;
+  if (java::lang::Thread::interrupted())
+    {
+      java::io::InterruptedIOException *iioe =
+	new java::io::InterruptedIOException (JvNewStringUTF ("read interrupted"));
+      iioe->bytesTransferred = r == -1 ? 0 : r;
+      throw iioe;
+    }
+  else if (r == -1)
+    {
+      //some errors cause us to return end of stream
+      if (errno == ENOTCONN 
+	  || errno == ENETDOWN 
+	  || errno == ENETUNREACH 
+	  || errno == ENETRESET
+	  || errno == ECONNABORTED
+	  || errno == ECONNRESET 
+	  || errno == ETIMEDOUT)
+	{
+	  fnum = -1;
+	  return -1;
+	}
+      else
+	throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+    }
+  return b & 0xFF;
+}
+
+//read count bytes into the buffer, starting at offset
+jint
+java::net::PlainSocketImpl::read (jbyteArray buffer, jint offset, jint count)
+{
+  if (! buffer)
+    throw new java::lang::NullPointerException;
+  jsize bsize = JvGetArrayLength (buffer);
+  if (offset < 0 || count < 0 || offset + count > bsize)
+    throw new java::lang::ArrayIndexOutOfBoundsException;
+  jbyte *bytes = elements (buffer) + offset;
+
+  //do timeouts via select
+  if (timeout > 0)
+  {
+    //create the file descriptor set
+    fd_set read_fds;
+    FD_ZERO( &read_fds);
+    FD_SET(fnum, &read_fds);
+    //create the timeout struct based on our internal timeout value
+    struct timeval timeout_value;
+    timeout_value.tv_sec = timeout/1000;
+    timeout_value.tv_usec = (timeout%1000)*1000;
+    //select on the fds
+    int sel_retval = _Jv_select(fnum+1, &read_fds, NULL, NULL, &timeout_value);
+    //we're only interested in the 0 return
+    //error returns still require us to try to read the socket to see what happened
+    if (sel_retval == 0)
+      {
+	java::io::InterruptedIOException *iioe =
+	  new java::io::InterruptedIOException (JvNewStringUTF ("read interrupted"));
+	iioe->bytesTransferred = 0;
+	throw iioe;
+      }
+  }
+  //read the socket
+  int r = ::recv (fnum, bytes, count, 0);
+
+  if (r == 0)
+    return -1;
+  if (java::lang::Thread::interrupted())
+    {
+      java::io::InterruptedIOException *iioe =
+	new java::io::InterruptedIOException (JvNewStringUTF ("read interrupted"));
+      iioe->bytesTransferred = r == -1 ? 0 : r;
+      throw iioe;
+    }
+  else if (r == -1)
+    {
+      //some errors cause us to return end of stream
+      if (errno == ENOTCONN 
+	  || errno == ENETDOWN 
+	  || errno == ENETUNREACH 
+	  || errno == ENETRESET
+	  || errno == ECONNABORTED
+	  || errno == ECONNRESET 
+	  || errno == ETIMEDOUT)
+	{
+	  fnum = -1;
+	  return -1;
+	}
+      else
+	throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+    }
+  return r;
+}
+
+//how many bytes are available?
+jint
+java::net::PlainSocketImpl::available (void)
+{
+#if defined (FIONREAD) || defined (HAVE_SELECT)
+  long num = 0;
+  int r = 0;
+  bool num_set = false;
+
+#if defined (FIONREAD)
+  r = ::ioctl (fnum, FIONREAD, &num);
+  if (r == -1 && errno == ENOTTY)
+    {
+      // If the ioctl doesn't work, we don't care.
+      r = 0;
+      num = 0;
+    }
+  else
+    num_set = true;
+#elif defined (HAVE_SELECT)
+  if (fnum < 0)
+    {
+      errno = EBADF;
+      r = -1;
+    }
+#endif
+
+  if (r == -1)
+    {
+    posix_error:
+      JvThrow (new java::io::IOException (JvNewStringUTF (strerror (errno))));
+
+    }
+
+  // if we didn't get anything we can use select
+
+#if defined (HAVE_SELECT)
+  if (! num_set)
+    {
+      fd_set rd;
+      FD_ZERO (&rd);
+      FD_SET (fnum, &rd);
+      struct timeval tv;
+      tv.tv_sec = 0;
+      tv.tv_usec = 0;
+      r = _Jv_select (fnum + 1, &rd, NULL, NULL, &tv);
+      if (r == -1)
+	goto posix_error;
+      num = r == 0 ? 0 : 1;
+    }
+#endif /* HAVE_SELECT */
+
+  return (jint) num;
+#else
+  JvThrow (new java::io::IOException (JvNewStringUTF ("unimplemented")));
+#endif
+}
+
+
+//option handling
 
 void
 java::net::PlainSocketImpl::setOption (jint optID, java::lang::Object *value)

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]