Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 5076772
Votes 12
Synopsis (se) Select.select(timeout) throws NullPointerException occasionally (win)
Category java:classes_nio
Reported Against 1.4.2
Release Fixed mustang(b75), 5.0u10(b01) (Bug ID:2138800)
State 10-Fix Delivered, bug
Priority: 3-Medium
Related Bugs 6434287
Submit Date 21-JUL-2004
Description



FULL PRODUCT VERSION :
java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)

FULL OS VERSION :
 customer  Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
Select.select(timeout) throws NullPointerException occasionally.
This would appear to be the same bug as Bug ID 4726620.
This bug still happens under j2sdk1.4.2

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
import java.io.*;
import java.net.*;
import java.nio.channels.*;
import java.util.*;

public class Test {
 public static void main(String[] args) throws IOException {
   final Selector selector = Selector.open();
   ServerSocketChannel serverChannel = ServerSocketChannel.open();
   serverChannel.configureBlocking(false).register(selector, SelectionKey.OP_ACCEPT);
   serverChannel.socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), 3333));
   startClientThread();
   while (true) {
     selector.select(1000); // NullPointerException in code called from this line
     synchronized (Test.class) {
       Iterator keyIterator = selector.selectedKeys().iterator();
       while (keyIterator.hasNext()) {
         SelectionKey key = (SelectionKey)keyIterator.next();
         keyIterator.remove();
         if (key.isAcceptable()) {
           final SelectionKey clientKey = serverChannel.accept().configureBlocking(false).register(selector, 0);
           new Thread() {
             public void run() {
               try {
                 Thread.sleep(10000);
               }
               catch (InterruptedException ex) {
               }
               synchronized (Test.class) {
                 clientKey.interestOps(SelectionKey.OP_WRITE);
               }
             }
           }.start();
         }
         if (key.isWritable()) {
           key.interestOps(0);
         }
       }
     }
   }
 }

 private static void startClientThread() throws IOException {
   new Thread() {
     public void run() {
       try {
         Selector selector = Selector.open();
         SocketChannel channel = SocketChannel.open();
         channel.configureBlocking(false);
         if (channel.connect(new InetSocketAddress(InetAddress.getLocalHost(), 3333))) {
           channel.register(selector, SelectionKey.OP_READ);
         }
         else {
           channel.register(selector, SelectionKey.OP_CONNECT);
         }
         while (true) {
           selector.select();
           synchronized (selector) {
             Iterator keyIterator = selector.selectedKeys().iterator();
             while (keyIterator.hasNext()) {
               SelectionKey key = (SelectionKey)keyIterator.next();
               keyIterator.remove();
               if (key.isConnectable()) {
                 if (channel.finishConnect()) {
                   key.interestOps(SelectionKey.OP_READ);
                 }
               }
               if (key.isReadable()) {
                 key.interestOps(0);
               }
             }
           }
         }
       }
       catch (IOException ex) {
         System.exit(1);
       }
     }
   }.start();
 }
}

As it only happens occasionaly this code might not reproduce the problem.

Bugs 4726620, 4684585 appear to have the same problem, and LotsOfChannels.java from bug 4685526 appears to reproduce this problem as well. 

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no exception
ACTUAL -
NullPointerException

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.NullPointerException
	at sun.nio.ch.WindowsSelectorImpl$SubSelector.processFDSet(WindowsSelectorImpl.java:309)
	at sun.nio.ch.WindowsSelectorImpl$SubSelector.processSelectedKeys(WindowsSelectorImpl.java:282)
	at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$2600(WindowsSelectorImpl.java:245)
	at sun.nio.ch.WindowsSelectorImpl.updateSelectedKeys(WindowsSelectorImpl.java:427)
	at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:142)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:59)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:70)

REPRODUCIBILITY :
This bug can be reproduced occasionally.


(Incident Review ID: 198556) 
======================================================================
Posted Date : 2006-01-13 08:57:07.0

Suggested fix provided by java.net member mernst

A DESCRIPTION OF THE FIX :
This is a patch to fix two bugs in the NIO selector implementation that occur with concurrent selection and closing of channels. This patch is against Mustang b66.

5076772: NullPointerException
	at sun.nio.ch.WindowsSelectorImpl$FdMap.remove(WindowsSelectorImpl.java:76)
	at sun.nio.ch.WindowsSelectorImpl$FdMap.access$3100(WindowsSelectorImpl.java:66)
	at sun.nio.ch.WindowsSelectorImpl.implDereg(WindowsSelectorImpl.java:524)
	at sun.nio.ch.SelectorImpl.processDeregisterQueue(SelectorImpl.java:131)
	at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:149)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)

is due to a missing null-check in FdMap.

  Patch
diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	2006-01-10 22:41:23.599350400 +0100
+++ src/windows/classes/sun/nio/ch/WindowsSelectorImpl.java	2006-01-14 13:18:18.638014400 +0100
@@ -73,7 +73,7 @@
         private MapEntry remove(SelectionKeyImpl ski) {
             Integer fd = new Integer(ski.channel.getFDVal());
             MapEntry x = get(fd);
-            if (x.ski.channel == ski.channel)
+            if (x != null && x.ski.channel == ski.channel)
                 return remove(fd);
             return null;
         }


4729342 Selector.select() throws CancelledKeyException seems to not have been completely fixed. While the nio* operations in SelectionKeyImpl provide access to the interest and ready ops without validity checking, they aren't used consistently in the various implementations of SelChImpl#translateAndUpdateReadyOps. The patch replaces all calls to #readyOps by #nioReadyOps.

diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/windows/classes/sun/nio/ch/SinkChannelImpl.java src/windows/classes/sun/nio/ch/SinkChannelImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/windows/classes/sun/nio/ch/SinkChannelImpl.java	2006-01-10 22:41:23.589336000 +0100
+++ src/windows/classes/sun/nio/ch/SinkChannelImpl.java	2006-01-14 13:27:10.422683200 +0100
@@ -78,7 +78,7 @@
     }
 
     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
-        return translateReadyOps(ops, sk.readyOps(), sk);
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
     }
 
     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/windows/classes/sun/nio/ch/SourceChannelImpl.java src/windows/classes/sun/nio/ch/SourceChannelImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/windows/classes/sun/nio/ch/SourceChannelImpl.java	2006-01-10 22:41:23.599350400 +0100
+++ src/windows/classes/sun/nio/ch/SourceChannelImpl.java	2006-01-14 13:27:10.462740800 +0100
@@ -77,7 +77,7 @@
     }
 
     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
-        return translateReadyOps(ops, sk.readyOps(), sk);
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
     }
 
     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/share/classes/sun/nio/ch/DatagramChannelImpl.java src/share/classes/sun/nio/ch/DatagramChannelImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	2006-01-10 22:39:38.468179200 +0100
+++ src/share/classes/sun/nio/ch/DatagramChannelImpl.java	2006-01-14 13:27:10.432697600 +0100
@@ -633,7 +633,7 @@
     }
 
     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
-        return translateReadyOps(ops, sk.readyOps(), sk);
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
     }
 
     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	2006-01-10 22:39:38.558308800 +0100
+++ src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	2006-01-14 13:27:10.452726400 +0100
@@ -253,7 +253,7 @@
     }
 
     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
-        return translateReadyOps(ops, sk.readyOps(), sk);
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
     }
 
     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/share/classes/sun/nio/ch/SocketChannelImpl.java src/share/classes/sun/nio/ch/SocketChannelImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/share/classes/sun/nio/ch/SocketChannelImpl.java	2006-01-10 22:39:38.568323200 +0100
+++ src/share/classes/sun/nio/ch/SocketChannelImpl.java	2006-01-14 13:27:10.462740800 +0100
@@ -716,7 +716,7 @@
     }
 
     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
-        return translateReadyOps(ops, sk.readyOps(), sk);
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
     }
 
     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java src/solaris/classes/sun/nio/ch/SinkChannelImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/solaris/classes/sun/nio/ch/SinkChannelImpl.java	2006-01-10 22:41:13.905411200 +0100
+++ src/solaris/classes/sun/nio/ch/SinkChannelImpl.java	2006-01-14 15:40:22.164243200 +0100
@@ -118,7 +118,7 @@
     }
 
     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
-        return translateReadyOps(ops, sk.readyOps(), sk);
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
     }
 
     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
diff -uwr /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java src/solaris/classes/sun/nio/ch/SourceChannelImpl.java
--- /home/Matthias/c/Program Files/Java/jdk1.6.0b66/j2se/src/solaris/classes/sun/nio/ch/SourceChannelImpl.java	2006-01-10 22:41:13.905411200 +0100
+++ src/solaris/classes/sun/nio/ch/SourceChannelImpl.java	2006-01-14 15:40:22.154228800 +0100
@@ -118,7 +118,7 @@
     }
 
     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
-        return translateReadyOps(ops, sk.readyOps(), sk);
+        return translateReadyOps(ops, sk.nioReadyOps(), sk);
     }
 
     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {


JUnit TESTCASE :
The attached test case is a standalone server that opens a server socket, and repeatedly connects to itself and closes the connection again in five concurrent clients. It just needs to be compiled and run.

package test;

import java.io.IOException;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SelectorStressTest {
  static Selector selector;
  static ExecutorService pool = Executors.newCachedThreadPool();
  static SocketAddress address;

  static class ChannelToSelect {
    static ConcurrentLinkedQueue<ChannelToSelect> queue = new ConcurrentLinkedQueue<ChannelToSelect>();

    public static void add(SelectableChannel channel, Protocol protocol) {
      ChannelToSelect ct = new ChannelToSelect();
      ct.channel = channel;
      ct.protocol = protocol;
      queue.add(ct);
    }

    public static ChannelToSelect poll() {
      return queue.poll();
    }

    SelectableChannel channel;
    Protocol protocol;
  }

  interface Protocol {
    public abstract int interest();
    public abstract Protocol run(Channel channel) throws IOException;
  }

  static Protocol ACCEPTING_SERVER = new Protocol() {
    public int interest() {
      return SelectionKey.OP_ACCEPT;
    }

    public Protocol run(Channel channel) throws IOException {
      SocketChannel connection;
        while((connection = ((ServerSocketChannel) channel).accept()) != null) {
          connection.configureBlocking(false);
          ChannelToSelect.add(connection, CONNECTED_SERVER);
        }

        return ACCEPTING_SERVER;
      }
    };

  static Protocol CONNECTED_SERVER = new Protocol() {
    public int interest() {
      return SelectionKey.OP_WRITE;
    }

    public Protocol run(Channel channel) {
      return null;
    }
  };

  static Protocol CONNECTING_CLIENT = new Protocol() {
    public int interest() {
      return SelectionKey.OP_CONNECT;
    }

    public Protocol run(Channel channel) throws IOException {
      return (((SocketChannel)channel).finishConnect()) ? CONNECTED_CLIENT : CONNECTING_CLIENT;
    }
  };

  static Protocol CONNECTED_CLIENT = new Protocol() {
    public int interest() {
      return SelectionKey.OP_READ;
    }

    public Protocol run(Channel channel) throws IOException {
      int bytes = ((SocketChannel) channel).read(ByteBuffer.allocate(1));
      if(bytes == -1) {
        startClient();
        return null;
      }

      return CONNECTED_CLIENT;
    }
  };

  private static void startClient() throws IOException {
    SocketChannel client = SocketChannel.open();
    client.configureBlocking(false);
    client.connect(address);
    ChannelToSelect.add(client, CONNECTING_CLIENT);
  }

  private static SocketAddress startServer() throws IOException {
    final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(false);
    serverSocketChannel.socket().bind(new InetSocketAddress(0));
    ChannelToSelect.add(serverSocketChannel, ACCEPTING_SERVER);
    return serverSocketChannel.socket().getLocalSocketAddress();
  }

  public static void main(String[] args) throws IOException {
    selector = Selector.open();
    address = startServer();
    startClient();
    startClient();
    startClient();
    startClient();
    startClient();

    while(true) {
      registerAndUpdateChannels();
      select();
      runSelected();
    }
  }

  private static void runSelected() {
    for (final SelectionKey key : selector.selectedKeys()) {
      key.interestOps(0);
      pool.execute(new Runnable() {
        public void run() {
          Protocol p = (Protocol) key.attachment();
          Protocol next;
          try {
            next = p.run(key.channel());
          } catch(IOException io) {
            next = null;
          }

          if(next == null) {
            try {
              key.channel().close();
            } catch (IOException e) {
            }
          } else {
            ChannelToSelect.add(key.channel(), next);
          }

          selector.wakeup();
        }
      });
    }
    selector.selectedKeys().clear();
  }

  private static void select() throws IOException {
    selector.select();
  }

  private static void registerAndUpdateChannels() throws ClosedChannelException {
    ChannelToSelect channelToSelect;
    while((channelToSelect = ChannelToSelect.poll()) != null)
      channelToSelect.channel.register(selector, channelToSelect.protocol.interest(), channelToSelect.protocol);
  }
}


FIX FOR BUG NUMBER:
4729342 5076772
Posted Date : 2006-01-14 16:54:46.0
Work Around
N/A
Evaluation
Not for Tiger.  --   xxxxx@xxxxx   2004/7/25
This specific NPE may have been resolved by the changes for 4892104 in 5.0. However there is still potential for issues with SocketChannels that are closed while still registered with a Selector. We received the following from   xxxxx@xxxxx  .

Exception in thread "pool-2-thread-6" java.lang.NullPointerException
	at sun.nio.ch.WindowsSelectorImpl$FdMap.remove(WindowsSelectorImpl.java:80)
	at sun.nio.ch.WindowsSelectorImpl$FdMap.access$3100(WindowsSelectorImpl.java:66)
	at sun.nio.ch.WindowsSelectorImpl.implDereg(WindowsSelectorImpl.java:528)
	at sun.nio.ch.SelectorImpl.processDeregisterQueue(SelectorImpl.java:132)
	at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:124)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:84)
	at test.nio.IODispatcher$2.run(IODispatcher.java:44)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:669)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:694)
	at java.lang.Thread.run(Thread.java:626)

To arrive at this NPE is complex but we can demonstrate that the following can arise:

1. SocketChannel A, based on Windows SOCKET S, is registered with a Selector
2. SocketChannel A is closed leaving an entry in the fdMap to map S to the SelectionKey
3. A new SocketChannel B is created with Windows SOCKET S
4. SocketChannel B is also registered with the Selector which will override the entry for S in the fdMap.
5. SocketChannel B is closed.
6. A select occurs and the deregistration queue is processed. The deregistration will attempt to remove two entries from the fdMap for S - the second will NPE with the above exception.

With this issue fixed there are other lurking issues.   xxxxx@xxxxx   also reported the following:

Exception in thread "pool-2-thread-3" java.nio.channels.CancelledKeyException
	at sun.nio.ch.SelectionKeyImpl.ensureValid(SelectionKeyImpl.java:55)
	at sun.nio.ch.SelectionKeyImpl.readyOps(SelectionKeyImpl.java:69)
	at sun.nio.ch.SocketChannelImpl.translateAndUpdateReadyOps(SocketChannelImpl.java:719)
	at sun.nio.ch.WindowsSelectorImpl$SubSelector.processFDSet(WindowsSelectorImpl.java:357)
	at sun.nio.ch.WindowsSelectorImpl$SubSelector.processSelectedKeys(WindowsSelectorImpl.java:302)
	at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$2900(WindowsSelectorImpl.java:260)
	at sun.nio.ch.WindowsSelectorImpl.updateSelectedKeys(WindowsSelectorImpl.java:452)
	at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:154)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:76)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:87)
	at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:91)
	at test.nio.IODispatcher$2.run(IODispatcher.java:44)
	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:669)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:694)
	at java.lang.Thread.run(Thread.java:626)

In this case it appears that the SocketChannel was closed just before the selected keys are updated. In that case a cancelled SelectionKey is encountered leading to the above.
Posted Date : 2006-01-13 08:57:07.0

Contribution-forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?forumID=1463&messageID=10889
Posted Date : 2006-01-14 16:54:46.0

--

This fix is targetted for mustang b75.
Posted Date : 2006-02-14 10:49:18.0
Comments
  
  Include a link with my name & email   

Submitted On 30-AUG-2004
djshaffer
Does anyone who has looked at this problem know about a workaround?  Is it safe to catch the exception and retry the call, or is some internal state hosed?


Submitted On 09-OCT-2006
The_Crusher
Kudos for the fix, but could we please get a back port of this bug to Java 5? We are getting bitten by the bug in sun.nio.ch.WindowsSelectorImpl$FdMap.remove and there seems to be no workaround available. Our application cannot perform efficiently with blocking I/O, so we would really appreciate to see this in a stable release of Java. It wouldn't be serious of us to force our customers to use a Java 6 beta at this stage.

Also, if anybody happens to know of a workaround, please contact me directly. Catching the exception and retrying the operation does not seem to help here: it gets past the exception, but I/O starts acting up.


Submitted On 09-OCT-2006
The_Crusher
Well, I posted the message above and ticked the checkbox so it would include my e-mail, but it didn't. Please contact me to this address (if this works this time).



PLEASE NOTE: JDK6 is formerly known as Project Mustang