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


The attached does what I described on the main list.

I'm going to assign (c) once I get the forms.


Nic Ferrier
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/06/30 19:10:29
@@ -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,16 @@
                    _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>
+   */
   int fnum = -1;
 
   // This value is set/read by setOption/getOption.
@@ -62,37 +74,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 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/06/30 19:10:31
@@ -70,6 +70,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 +114,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 +353,230 @@
   char* strerr = strerror (errno);
   throw new java::io::IOException (JvNewStringUTF (strerr));
 }
+
+//close (shutdown) the socket
+void
+java::net::PlainSocketImpl::close ()
+{
+  int res = ::shutdown(fnum,2);
+  if (res == -1)
+    throw new java::io::IOException( JvNewStringUTF (strerror (errno)) );
+}
+
+/*
+  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;
+    }
+  if (java::lang::Thread::interrupted())
+    {
+      java::io::InterruptedIOException *iioe
+	= new java::io::InterruptedIOException (JvNewStringUTF ("write interrupted"));
+      iioe->bytesTransferred = r == -1 ? 0 : r;
+      JvThrow (iioe);
+    }
+  else if (r == -1)
+    JvThrow (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)
+    JvThrow (new java::lang::NullPointerException);
+  if (offset < 0 || len < 0 || offset + len > JvGetArrayLength (b))
+    JvThrow (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;
+      JvThrow (iioe);
+    }
+  else if (r == -1)
+    JvThrow (new java::io::IOException (JvNewStringUTF (strerror (errno))));
+  // FIXME: loop if r != len.
+}
+
+
+/*
+  the non-blocking performance of reads could be improved by 
+  caching the fd_set's needed for each socket.
+
+  Nic Ferrier - June 2001
+*/
+
+//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;
+    if ((sel_retval = _Jv_select(fnum+1, &read_fds, NULL, NULL, &timeout_value)) < 0)
+      goto do_read;
+    else if(sel_retval == 0)
+      throw new java::io::InterruptedIOException( JvNewStringUTF("read timed out"));
+    //note that we don't need to examine the retval further
+    //because we only asked about one file descriptor
+  }
+ do_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;
+      JvThrow (iioe);
+    }
+  else
+    if (r == -1)
+      JvThrow (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)
+    JvThrow (new java::lang::NullPointerException);
+  jsize bsize = JvGetArrayLength (buffer);
+  if (offset < 0 || count < 0 || offset + count > bsize)
+    JvThrow (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;
+    if ((sel_retval = _Jv_select(fnum+1, &read_fds, NULL, NULL, &timeout_value)) < 0)
+      goto do_read;
+    else if(sel_retval == 0)
+      throw new java::io::InterruptedIOException( JvNewStringUTF("read timed out"));
+    //note that we don't need to examine the retval further
+    //because we only asked about one file descriptor
+  }
+ do_read:
+  int r = ::read (fnum, bytes, count);
+  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;
+      JvThrow (iioe);
+    }
+  else if (r == -1)
+    JvThrow (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]