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]
Other format: [Raw text]

Socket timeout patch




Enclosed within this message is the diff for the socket timeout
patch. This has been a long time coming for such a small patch, I hope
that everyone will be happy with the formatting!


This is the changelog entry for the patch:

2001-12-14  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



Nic


The rest of the text is the diff:

Index: 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/12/15 22:09:33
@@ -11,17 +11,17 @@
 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
  * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
  * Status:  Believed complete and correct.
+ *
+ * @author Per Bothner <bothner@cygnus.com>
+ * @date February 22, 1999.
+ * @author Nic Ferrier <nferrier@tapsellferrier.co.uk>
+ * @date December 15, 2001
  */
-
 class PlainSocketImpl extends SocketImpl
 {
   // These fields are mirrored for use in native code to avoid cpp conflicts
@@ -35,6 +35,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 +74,132 @@
   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 native void close () 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;
 
-  private InputStream in;
-  private OutputStream out;
 
+  // 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;
+
+
+  // The native write methods.
+
+  private native void write(int c) throws IOException;
+
+  private native void write(byte[] buffer, int offset, int count)
+    throws IOException;
+
+
+  /** @return the input stream attached to the socket.
+   */
   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();
     return in;
   }
 
+  /** @return the output stream attached to the socket.
+   */
   protected OutputStream getOutputStream() throws IOException
   {
     if (out == null)
-      out = new FileOutputStream (fd);
+      out = new SocketOutputStream();
     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
+  {
+    SocketInputStream()
+    {
+    }
+    
+    public void close() throws IOException
+    {
+      PlainSocketImpl.this.close();
+    }
+
+    public int available() throws IOException
+    {
+      return PlainSocketImpl.this.available();
+    }
+
+    public int read() throws IOException
+    {
+      return PlainSocketImpl.this.read();
+    }
+
+    public int read(byte[] buffer, int offset, int length)
+      throws IOException
+    {
+      return PlainSocketImpl.this.read(buffer, offset, length);
+    }
+
+    public int read(byte[] buffer)
+      throws IOException
+    {
+      return PlainSocketImpl.this.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
+  {
+    SocketOutputStream()
+    {
+    }
+    
+    public void close() throws IOException
+    {
+      PlainSocketImpl.this.close();
+    }
+
+    public void write(int c) throws IOException
+    {
+      PlainSocketImpl.this.write(c);
+    }
+
+    public void write(byte[] buffer, int offset, int length)
+      throws IOException
+    {
+      PlainSocketImpl.this.write(buffer, offset, length);
+    }
+
+    public void write(byte[] buffer)
+      throws IOException
+    {
+      PlainSocketImpl.this.write(buffer, 0, buffer.length);
+    }
   }
 }
Index: natPlainSocketImpl.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/net/natPlainSocketImpl.cc,v
retrieving revision 1.25
diff -u -r1.25 natPlainSocketImpl.cc
--- natPlainSocketImpl.cc	2001/08/01 17:53:00	1.25
+++ natPlainSocketImpl.cc	2001/12/15 22:09:35
@@ -85,6 +85,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$
 
@@ -326,6 +329,258 @@
   char* strerr = strerror (errno);
   throw new java::io::IOException (JvNewStringUTF (strerr));
 }
+
+// Close(shutdown) the socket.
+void
+java::net::PlainSocketImpl::close()
+{
+  // should we use shutdown here? how would that effect so_linger?
+  int res = ::close (fnum);
+
+  if (res == -1)
+    {
+      // These three errors are not errors according to tests performed
+      // on the reference implementation.
+      if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF)
+	throw new java::io::IOException  (JvNewStringUTF (strerror (errno)));
+    }
+  // Safe place to reset the file pointer.
+  fnum = -1;
+}
+
+// Write a byte to the socket.
+void
+java::net::PlainSocketImpl::write(jint b)
+{
+  jbyte d =(jbyte) b;
+  int r = 0;
+
+  while (r != 1)
+    {
+      r = ::write (fnum, &d, 1);
+      if (r == -1)
+	{
+	  if (java::lang::Thread::interrupted())
+	    {
+	      java::io::InterruptedIOException *iioe
+		= new java::io::InterruptedIOException 
+		(JvNewStringLatin1 (strerror (errno)));
+	      iioe->bytesTransferred = 0;
+	      throw iioe;
+	    }
+	  // Some errors should not cause exceptions.
+	  if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF)
+	    throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+	}
+    }
+}
+
+// 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 written = 0;
+  while (len > 0)
+    {
+      int r = ::write (fnum, bytes, len);
+      if (r == -1)
+        {
+	  if (java::lang::Thread::interrupted())
+	    {
+	      java::io::InterruptedIOException *iioe
+		= new java::io::InterruptedIOException
+		(JvNewStringLatin1 (strerror (errno)));
+	      iioe->bytesTransferred = written;
+	      throw iioe;
+	    }
+	  // Some errors should not cause exceptions.
+	  if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF)
+	    throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+	}
+      written += r;
+      len -= r;
+      bytes += r;
+    }
+}
+
+
+// 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 select returns 0 we've waited without getting data...
+    // that means we've timed out.
+    if (sel_retval == 0)
+      throw new java::io::InterruptedIOException
+	(JvNewStringUTF ("read timed out") );
+    // If select returns ok 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)
+    {
+      if (errno == ECONNRESET)
+	throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+      // Some errors cause us to return end of stream...
+      if (errno == ENOTCONN)
+	return -1;
+      // Other errors need to be signalled.
+      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)
+    {
+      if (errno == ECONNRESET)
+	throw new java::io::IOException (JvNewStringUTF (strerror (errno)));
+      // Some errors cause us to return end of stream...
+      if (errno == ENOTCONN)
+	return -1;
+      // Other errors need to be signalled.
+      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:
+      throw 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
+  throw new java::io::IOException (JvNewStringUTF ("unimplemented"));
+#endif
+ }
+
 
 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]