United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6645197 (so) Timed read with socket adaptor throws ClosedSelectorException if temporary selector GC'ed.
JDK-6645197 : (so) Timed read with socket adaptor throws ClosedSelectorException if temporary selector GC'ed.

Details
Type:
Bug
Submit Date:
2007-12-24
Status:
Closed
Updated Date:
2011-05-18
Project Name:
JDK
Resolved Date:
2011-05-18
Component:
core-libs
OS:
generic
Sub-Component:
java.nio
CPU:
other
Priority:
P3
Resolution:
Fixed
Affected Versions:
5.0u24,6u3,6u20
Fixed Versions:

Related Reports
Backport:
Backport:
Backport:
Backport:
Backport:
Backport:
Relates:

Sub Tasks

Description
OPERATING SYSTEM(S)
-------------------
First seen on Windows. Failure is also reported on Linux IA32.

FULL JDK VERSION
----------------
java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Client VM (build 1.6.0_03-b05, mixed mode)

Also fails on 5.0.

DESCRIPTION
-----------
When a SocketChannel is used as the base for a socket InputStream and read() is called, it's possible for the read() to throw  a ClosedSelectorException.  The exception is thrown if the read() blocks for a long time and the GC runs (at least once) during the block.       
                                                                         
If the socket has a non-0 timeout then the read() call gets turned into Selector.poll() in the implementation.  If the GC runs and decides that SelectWrapper's phantom reference should be cleaned up then a Cleanup associated with this Seletor Wrapper is going ahead and closing the Temporary Selector associated with the Thread before read could finish.
                                  
The reason we do not see the exception when timeout is not set is because this temporary selector is not obtained in the first place,it just tries to read into the buffer.                                                         
                                                                         
The temporary selector getting lost should not get lost before the testcase is done using it.

RECREATION INSTRUCTIONS
-----------------------

Run the attached testcase with a reduced amount of memory (ie, with -Xmx5m or so) for faster recreate.

Exception stack trace:

Exception in thread "main" java.nio.channels.ClosedSelectorException     
       at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:66)  
       at sun.nio.ch.SelectorImpl.selectNow(SelectorImpl.java:88)        
       at sun.nio.ch.Util.releaseTemporarySelector(Util.java:135)        
       at sun.nio.ch.SocketAdaptor$SocketInputStream.read(SocketAdaptor.java:209)  
       at sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:86) 
       at java.io.InputStream.read(InputStream.java:85)                  
       at ClosedSelectorTest.main(ClosedSelectorTest.java:63)   

TESTCASE SOURCE
---------------
import java.io.IOException;                                              
import java.net.InetSocketAddress;                                       
import java.net.ServerSocket;                                            
import java.net.Socket;                                                  
import java.nio.channels.ServerSocketChannel;                            
import java.nio.channels.SocketChannel;                                  

public class ClosedSelectorTest {

    public static void main(String[] args) {                          
        try {
            // Create a server socket that will open and  accept                                             
            ServerSocketChannel serverSocketChannel =         
            ServerSocketChannel.open();                                              
            final ServerSocket serverSocket =                 
            serverSocketChannel.socket();                                            
            serverSocket.bind(null);                          
            int localPort = serverSocket.getLocalPort();      
            Thread acceptor = new Thread(new Runnable() {     
                                             public void run() {                       
                                                 try {
                                                     System.out.println("Waiting on serverSocket.accept()");           
                                                     System.out.flush();                                                                             Socket socket =  serverSocket.accept();                                                         System.out.println("Server socket connected");    
                                                     System.out.flush();       
                                                 } catch (IOException e) {
                                                     e.printStackTrace();      
                                                 }
                                             }                                         
                                         });                                               
            acceptor.start();                                 

            // Create a thread that just creates data to cause the                                                                
            // GC to run                                      
            Thread t = new Thread(new Runnable() {            
                                      public void run() {                       
                                          while (true) {
                                              byte[] bytes = new byte[100000];                                                                                         System.gc();              
                                          }                                 
                                      }                                         
                                  });                                               
            t.start();                                        

            // Create a client socket that will connect and read                                                                     
            System.out.println("Connecting to server socket");                                                                
            System.out.flush();                               
            SocketChannel channel = SocketChannel.open(new InetSocketAddress("localhost", localPort));                              
            System.out.println("Connected to server socket"); 

            System.out.flush();                               
            try {
                System.out.println("Reading from socket input stream");                                                          
                System.out.flush();                       
                byte[] buffer = new byte[500];            
                Socket socket = channel.socket();         
                socket.setSoTimeout(1000000);  // The timeout must be set                                                                                                                      
                // to trigger this bug                                 
                socket.getInputStream().read(buffer);     
            } finally {
                try {
                    channel.close();                  
                } catch (IOException e) {
                    e.printStackTrace();              
                }
            }                                                 
        } catch (IOException e) {
            e.printStackTrace();                              
        }
    }           
}

                                    

Comments
EVALUATION

Yes, this is a bug. Should only impact code using the socket adaptor for timed reads. Will fix in jdk7 first.
                                     
2007-12-24
EVALUATION

While the implementation does have a dedicated ThreadLocal variable localSelectorWrapper designed to hold a reference to the selWrapper object to prevent it from being cleaned when the temporary selector is on lease, unfortunately the selWrapper is NOT stored into this place-holder the first time the selector is being created/inited for lease, which caused the problem in scenario described in the description.
                                     
2008-06-27



Hardware and Software, Engineered to Work Together