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 (again)


I've changed the patch for socket timeouts again. This fixed version
handles closing the socket in exactly the same way as JDK's does (as
far as I can tell by test anyway).


Nic Ferrier
cvs server: Diffing libjava/java/net
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/08 02:27:06
@@ -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/08 02:27:08
@@ -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,262 @@
   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);
+
+  /***
+      FIXME!
+      This is the wierdest bug - if you leave this here
+      the ::close() will fail because fnum will have been
+      set to -1
+      Comment out this line and everything is fine.
+  //reset the socket fd
+  fnum = -1;
+  ***/
+
+  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;
+}
+
+
+/*
+  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 should not cause exceptions
+      if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF)
+	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 should not cause exceptions
+      if (errno != ENOTCONN && errno != ECONNRESET && errno != EBADF)
+	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)
+      //we've timed out - so throw the interrupted excep
+      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
+}
+
+
+//option handling
 
 void
 java::net::PlainSocketImpl::setOption (jint optID, java::lang::Object *value)
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 Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]