This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
PlainSocketImpl changes to support socket timeouts
- To: java-patches at gcc dot gnu dot org
- Subject: PlainSocketImpl changes to support socket timeouts
- From: "Nic Ferrier" <nferrier at tapsellferrier dot co dot uk>
- Date: Sat, 30 Jun 2001 20:09:32 +0100
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)