natPlainSocketImpl.cc: Added timeout handling for sockets.
authorNic Ferrier <nferrier@tf1.tapsellferrier.co.uk>
Tue, 8 Jan 2002 21:14:58 +0000 (21:14 +0000)
committerTom Tromey <tromey@gcc.gnu.org>
Tue, 8 Jan 2002 21:14:58 +0000 (21:14 +0000)
2002-01-08  Nic Ferrier  <nferrier@tf1.tapsellferrier.co.uk>

* java/net/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.
* java/net/PlainSocketImpl.java: Glue for new timeout
implementation.
(write): Call the native impl.
(read): Likewise.
(getInputStream): Get a stream to read from the socket.
(getOutputStream): Get a stream to write to the socket.

From-SVN: r48662

libjava/ChangeLog
libjava/java/net/PlainSocketImpl.java
libjava/java/net/natPlainSocketImpl.cc

index db22179..a5e7fa7 100644 (file)
@@ -1,3 +1,17 @@
+2002-01-08  Nic Ferrier  <nferrier@tf1.tapsellferrier.co.uk> 
+
+       * java/net/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.
+       * java/net/PlainSocketImpl.java: Glue for new timeout
+       implementation.
+       (write): Call the native impl.
+       (read): Likewise.
+       (getInputStream): Get a stream to read from the socket.
+       (getOutputStream): Get a stream to write to the socket.
+
 2002-01-08  Tom Tromey  <tromey@redhat.com>
 
        * resolve.cc (_Jv_PrepareClass): Enable verifier.
index 49155d9..81df487 100644 (file)
@@ -1,6 +1,6 @@
 // PlainSocketImpl.java - Implementation of SocketImpl.
 
-/* Copyright (C) 1999  Free Software Foundation
+/* Copyright (C) 1999 , 2002 Free Software Foundation
 
    This file is part of libgcj.
 
@@ -11,17 +11,16 @@ details.  */
 package java.net;
 import java.io.*;
 
-/**
- * @author Per Bothner <bothner@cygnus.com>
- * @date February 22, 1999.
- */
 
 /**
+ * The standard GCJ socket implementation.
  * 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>
+ * @author Nic Ferrier <nferrier@tapsellferrier.co.uk>
  */
-
 class PlainSocketImpl extends SocketImpl
 {
   // These fields are mirrored for use in native code to avoid cpp conflicts
@@ -35,6 +34,18 @@ class PlainSocketImpl extends SocketImpl
                    _Jv_SO_SNDBUF_ = SocketOptions.SO_SNDBUF,
                    _Jv_SO_RCVBUF_ = SocketOptions.SO_RCVBUF;
 
+  /**
+   * The OS file handle representing the socket.
+   * This is used for reads and writes to/from the socket and
+   * to close it.
+   *
+   * {@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 +73,127 @@ class PlainSocketImpl extends SocketImpl
   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 int available() throws IOException;
+
+  protected native void close () throws IOException;
+
+
+  // Stream handling.
+
+  /** A cached copy of the in stream for reading from the socket.  */
   private InputStream in;
+
+  /** A cached copy of the out stream for writing to the socket.  */
   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;
+
+
+  // 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);
+      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
+  /**
+   * A stream which reads from the socket implementation.
+   *
+   * @author Nic Ferrier <nferrier@tapsellferrier.co.uk>
+   */
+  class SocketInputStream
+    extends InputStream
   {
-    return in.available();
+    SocketInputStream()
+    {
+    }
+    
+    public final void close() throws IOException
+    {
+      PlainSocketImpl.this.close();
+    }
+
+    public final int available() throws IOException
+    {
+      return PlainSocketImpl.this.available();
+    }
+
+    public final int read() throws IOException
+    {
+      return PlainSocketImpl.this.read();
+    }
+
+    public final int read(byte[] buffer, int offset, int length)
+      throws IOException
+    {
+      return PlainSocketImpl.this.read(buffer, offset, length);
+    }
+
+    public final int read(byte[] buffer)
+      throws IOException
+    {
+      return PlainSocketImpl.this.read(buffer, 0, buffer.length);
+    }
   }
 
-  protected void close () throws IOException
+  /** A stream which writes to the socket implementation.
+   *
+   * @author Nic Ferrier  <nferrier@tapsellferrier.co.uk>
+   */
+  class SocketOutputStream
+    extends OutputStream
   {
-    if (fd.valid())
-      fd.close();
+    public final void close() throws IOException
+    {
+      PlainSocketImpl.this.close();
+    }
+
+    public final void write(int c) throws IOException
+    {
+      PlainSocketImpl.this.write(c);
+    }
+
+    public final void write(byte[] buffer, int offset, int length)
+      throws IOException
+    {
+      PlainSocketImpl.this.write(buffer, offset, length);
+    }
+
+    public final void write(byte[] buffer)
+      throws IOException
+    {
+      PlainSocketImpl.this.write(buffer, 0, buffer.length);
+    }
   }
 }
index 0367d4f..cc8dca1 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000 , 2002 Free Software Foundation
 
    This file is part of libgcj.
 
@@ -85,6 +85,9 @@ _Jv_accept (int fd, struct sockaddr *addr, socklen_t *addrlen)
 #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$
 
@@ -327,6 +330,254 @@ java::net::PlainSocketImpl::accept (java::net::PlainSocketImpl *s)
   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)
+    {
+      // 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)
+    {
+      // 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)
 {