This is the mail archive of the
java@gcc.gnu.org
mailing list for the Java project.
Re: Unblocking blocked I/O
David Vrabel wrote:
>Yeah. I found that discussion. So much for the claimed cross-platform
>compatibility...
>
>>Unblocking I/O by closing a socket (resource revocation), however,
>>should work, and its a bug if it doesn't. Do you have a test case?
>>
>Yes. It's perhaps a bit long but find it attached. Hmm. Attachments are
>okay aren't they?
>
Thanks. I tried this on both Sun's Linux 1.3.1 JDK and IBM's Linux JDK
1.3.0.
Sun's JDK appears to implement neither behaviour - both
thread.interrupt() and socket.close() have no effect. On the IBM JDK,
socket.close() has no effect but thread.interrupt() does cause the
reading thread to unblock with an InterruptedIOException.
Here's a note from the 1.4 beta API docs (from java.nio.channels.Channel):
> That channels are asynchronously closeable and interruptible arises
> from the practical need to be able to break threads out of blocking
> I/O operations.
>
> The java.io
> <http://waitaki/docs/jdk1.4/docs/api/java/io/package-summary.html>
> package specifies an | InterruptedIOException|
> <http://waitaki/docs/jdk1.4/docs/api/java/io/InterruptedIOException.html>
> for this purpose, the idea being that invoking a thread's | interrupt|
> <http://waitaki/docs/jdk1.4/docs/api/java/lang/Thread.html#interrupt%28%29>
> method will cause the thread to break out of a blocking I/O operation
> and receive this exception. Unfortunately this approach has turned out
> to be impossible to implement both efficiently and reliably on most
> popular operating systems, and in fact it is well known not to work
> uniformly across different implementations of the Java platform.
>
> A technique that does work, however, is to close the file descriptor
> or handle that is the subject of the operation in which the target
> thread is blocked. Despite the fact that this closes the corresponding
> stream or channel, this behavior is often acceptable because the most
> common reason for interrupting a thread is to ask it to shut down. It
> is also difficult in practice to write systems that deal gracefully at
> a high level with partly-completed I/O operations; it is often simply
> easier to discard a stream or channel that's in an unknown state and
> recover in some other way, /e.g./, by opening a new connection. Hence
> this technique has come to be known as a portable workaround for the
> lack of true interruptible I/O in the java.io
> <http://waitaki/docs/jdk1.4/docs/api/java/io/package-summary.html>
> package.
>
So the docs are advocating the method that does not work on the Linux JDK's.
Ideally, in libgcj we should probibly implement both, but persumably
interrupt on close() is unimplemented (in the JDKs) because it is hard
to do on Linux: a close() on a file descriptor does not cause another
thread reading from that FD to unblock. We could implement this by
recording the current thread ID upon entry to read() etc, and have
close() set a flag and signal the reading thread. This would add some
overhead to every IO call (cost of looking up the current thread). I
don't know what sort of impact that would have on networking
performance, but if its insignificant then why havn't the other Linux
implementations done it?
It is easy to implement thread.interrupt() for IO however - this patch,
which I will check in, should give us similar behaviour to the IBM Linux
JDK. Hopefully the whole problem will go away when we implement java.nio
anyway because that gives us non-blocking IO as well as clearly defined
interrupt semantics.
regards
Bryce.
2001-09-20 Bryce McKinlay <bryce@waitaki.otago.ac.nz>
* posix-threads.cc (_Jv_ThreadInterrupt): Enable interrupt of
blocking IO via pthread_kill().
* java/io/natFileDescriptorPosix.cc (write (jint)): Check for thread
interrupted status flag only if ::write returned an error.
(write (jbyteArray, jint, jint): Likewise.
(read (jint)): Likewise.
(read (jbyteArray, jint, jint): Likewise.
Index: posix-threads.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/posix-threads.cc,v
retrieving revision 1.27
diff -u -r1.27 posix-threads.cc
--- posix-threads.cc 2001/05/24 05:40:36 1.27
+++ posix-threads.cc 2001/09/20 06:42:09
@@ -263,7 +263,7 @@
data->thread_obj->interrupt_flag = true;
// Interrupt blocking system calls using a signal.
-// pthread_kill (data->thread, INTR);
+ pthread_kill (data->thread, INTR);
pthread_cond_signal (&data->wait_cond);
Index: java/io/natFileDescriptorPosix.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/io/natFileDescriptorPosix.cc,v
retrieving revision 1.15
diff -u -r1.15 natFileDescriptorPosix.cc
--- natFileDescriptorPosix.cc 2001/08/02 23:46:39 1.15
+++ natFileDescriptorPosix.cc 2001/09/20 06:42:09
@@ -125,15 +125,17 @@
while (r != 1)
{
r = ::write (fd, &d, 1);
- if (java::lang::Thread::interrupted())
- {
- InterruptedIOException *iioe
- = new InterruptedIOException (JvNewStringLatin1 ("write interrupted"));
- iioe->bytesTransferred = r == -1 ? 0 : r;
- throw iioe;
+ if (r == -1)
+ {
+ if (java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
}
- else if (r == -1)
- throw new IOException (JvNewStringLatin1 (strerror (errno)));
}
}
@@ -150,18 +152,19 @@
while (len > 0)
{
int r = ::write (fd, bytes, len);
- if (r != -1)
- written += r;
- if (java::lang::Thread::interrupted())
- {
- InterruptedIOException *iioe
- = new InterruptedIOException (JvNewStringLatin1 ("write interrupted"));
- iioe->bytesTransferred = written;
- throw iioe;
+ if (r == -1)
+ {
+ if (java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = written;
+ throw iioe;
+ }
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
}
- else if (r == -1)
- throw new IOException (JvNewStringLatin1 (strerror (errno)));
+ written += r;
len -= r;
bytes += r;
}
@@ -222,15 +225,17 @@
int r = ::read (fd, &b, 1);
if (r == 0)
return -1;
- if (java::lang::Thread::interrupted())
+ if (r == -1)
{
- InterruptedIOException *iioe
- = new InterruptedIOException (JvNewStringLatin1 ("read interrupted"));
- iioe->bytesTransferred = r == -1 ? 0 : r;
- throw iioe;
+ if (java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
}
- else if (r == -1)
- throw new IOException (JvNewStringLatin1 (strerror (errno)));
return b & 0xFF;
}
@@ -246,15 +251,17 @@
int r = ::read (fd, bytes, count);
if (r == 0)
return -1;
- if (java::lang::Thread::interrupted())
- {
- InterruptedIOException *iioe
- = new InterruptedIOException (JvNewStringLatin1 ("read interrupted"));
- iioe->bytesTransferred = r == -1 ? 0 : r;
- throw iioe;
+ if (r == -1)
+ {
+ if (java::lang::Thread::interrupted())
+ {
+ InterruptedIOException *iioe
+ = new InterruptedIOException (JvNewStringLatin1 (strerror (errno)));
+ iioe->bytesTransferred = r == -1 ? 0 : r;
+ throw iioe;
+ }
+ throw new IOException (JvNewStringLatin1 (strerror (errno)));
}
- else if (r == -1)
- throw new IOException (JvNewStringLatin1 (strerror (errno)));
return r;
}