JDK-6938230 : (so) SocketAdaptor.close() does not translate IOException resulting in Error
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2010-03-25
  • Updated: 2013-05-25
  • Resolved: 2011-05-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b100Fixed
Related Reports
Relates :  
Description
OPERATING SYSTEM
----------------
All

FULL JDK VERSION
----------------
All

DESCRIPTION from LICENSEE:
--------------------------
Consider the creation of a SocketAdaptor instance using code like this:

    InetAddress loopback = InetAddress.getByName("192.168.1.1");
    SocketChannel sc = SocketChannel.open();
    
    InetSocketAddress addr = new InetSocketAddress(loopback,80);
    sc.socket().connect(addr);

If we subsequently attempt close the SocketAdaptor:
 
    sc.socket().close();

but the close() fails with an IOException (which can happen as per the API spec), the SocketAdaptor will rethrow the Exception as an Error:

    java.lang.Error: Untranslated exception
        at sun.nio.ch.Net.translateToSocketException(Net.java:91)
        at sun.nio.ch.SocketAdaptor.close(SocketAdaptor.java:403)
        ...

In practice this happens quite rarely, but when it does happen this unhandled Error causes the thread (or even the process) to die, and there is no real means of handling it, other than adding a very nasty catch(Throwable t) block.

The problem occurs because the SocketAdaptor class does not translate the IOException, and the default action is to throw an Error in this scenario. The relevant code, in SocketAdaptor.close(), looks like this:

    public void close() throws IOException {
        try {
            sc.close();
        } catch (Exception x) {
     ---->  Net.translateToSocketException(x);  <----
        }
    }

The IOException would be translated appropriately if the code was changed to this:

    public void close() throws IOException {
        try {
            sc.close();
        } catch (Exception x) {
     ---->  Net.translateException(x); <----
        }
    }

Note that the code in ServerSocketAdaptor.close() already looks like the fix suggestion above.  Also note essentially the same issue used to exist for BindExceptions in SocketAdaptor.bind() (CR 6303753) - this was fixed in exactly the way suggested above.

Unfortunately there is no easy way to test this, although the failing code looks something like this:

----
InetAddress loopback = InetAddress.getByName("192.168.1.1");
SocketChannel sc = SocketChannel.open();
   
InetSocketAddress addr = new InetSocketAddress(loopback,80);
sc.socket().connect(addr);
sc.socket().close();     <-- IOException needs to be thrown here
----

Following the execution path through the JDK source the IOException can only be thrown by the code below, in FileDispatcher.c (this is from the Solaris source):

----
static void closeFileDescriptor(JNIEnv *env, int fd) {
    if (fd != -1) {
        int result = close(fd);
        if (result < 0)
            JNU_ThrowIOExceptionWithLastError(env, "Close failed");
    }
}
----

The problem, in terms of recreation, is working out an artificial way of forcing the IOException to be thrown when close() is called.

Our customer is on Z/OS, and they are seeing the IOException with the message "EDC5112I Resource temporarily unavailable.". This is the Z/OS equivalent of the POSIX error "EAGAIN", so exactly the same issue could occur on Solaris/Linux under the right circumstances (i.e. where the file descriptor could not be accessed by the OS when close() is called).

As mentioned in the report, in production the IOException is highly intermittent, but when it does occur it's unnecessarily catastrophic because it ends up being translated to an Error by the SocketAdaptor code. If it was translated properly the Exception could be caught and handled accordingly.

Comments
EVALUATION We should fix SocketAdapter.close so that it simply invokes the channel's close method. There is no need to do any special handling or translation of the exception (same thing for ServerSocketAdapter.close).
10-05-2010

SUGGESTED FIX Here is a pointer to a prebuilt JDK 6u18+fix prototype containing IBM's fix suggestion. /net/jpelab-niagara.sfbay/export/home1/builds/6_18-1735714/j2se/build/solaris-sparc/bin IBM is requesting that we provide feedback on this fix so that they may integrate it into their codebase with confidence. IBM is not currently driving us to integrate the fix into the Oracle JDK.
25-03-2010