When close() is called on a Socket or ServerSocket, it should cause a SocketException to be thrown on any thread which is blocked on I/O operations on that socket. In libgcj, no exception is thrown, and the I/O thread remains blocked even though the socket has been closed. The attached test case demonstrates the problem for accept() and read(). The problem most likely occurs for other calls such as write() and connect() as well. This test case should be extended to cover these other calls, and added to mauve.
Created attachment 6278 [details] Test case
Confirmed.
FAIL: wrong exception: java.io.IOException: Bad file descriptor FAIL: read() did not interrupt on close(). FAIL: read() did not interrupt on close(). FAIL: accept() did not interrupt on close().
I'm working on it
Created attachment 10189 [details] Program that demonstrates how shutdown can solve the problem. Compile socktest.c thusly: gcc -g -o soctest soctest.c -lpthread Then run it and from a different window telnet localhost 4455 Do not type anything in the telnet window. When the socket is shutdown the read returns with a value of zero. I guess we should shutdown as well as close in the socket code.
Two things: Does this work for fds that aren't associated with sockets? It doesn't quite avoid the need for locking, since we still need to make sure that we only close an fd once.
Here is the first version of my patch: http://gcc.gnu.org/ml/java-patches/2005-q4/msg00176.html
New patch at: http://gcc.gnu.org/ml/java-patches/2005-q4/msg00179.html
Subject: Bug 15430 Author: daney Date: Tue Nov 15 19:11:53 2005 New Revision: 107036 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=107036 Log: PR libgcj/15430 * gnu/java/net/natPlainSocketImplPosix.cc (throw_on_sock_closed): New function. (accept): Call it. (close): Call shutdown before closing. (read()): Call read_helper with proper parameters. (read(buffer, int, int)): Likewise. (read_helper): Pass pointer to the PlainSocketImpl, remove native_fd and timeout parameters. Make prototype to match. Use pointer to PlainSocketImpl to access members. Call throw_on_sock_closed in two places. Modified: trunk/libjava/ChangeLog trunk/libjava/gnu/java/net/natPlainSocketImplPosix.cc
Fixed by committed patch.
I feel a potential race condition in this patch. What happens in that case ? thread 2: => PlainSocketImpl.accept => enters _Jv_select thread 1: => shutdown socket => close socket thread 3: => create another socket which occurs to have the same fd. thread 2: => select wakes up => we call accept with the old fd thread 1: => set native_fd to -1 I expect this scheme may be a flaw and it is not forbidden by the current code. A simple fix would be to add a synchronization lock before accepting the connection. Wrong ?