FULL PRODUCT VERSION :
java version " 1.7.0_09 "
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) Server VM (build 23.5-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
SunOS sundev31 5.10 144488-05 sun4v sparc SUNW,T5240
A DESCRIPTION OF THE PROBLEM :
It seems there was a previous issue with epoll spinning, for which the suggested workaround was to thrash the sector and create a new one and fix was available for Java 7. Is the fix available for all platforms ?
REGRESSION. Last worked in version 6u31
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a selector
2. Register OP_CONNECT, if AsyncSocket.connect() returned false
3. After connection gets created, unregister OP_CONNECT
4. Register OP_READ
5. The selector starts spining
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
// TimeClient.java [Connects and registers OP_READ]
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class TimeClient implements Runnable {
private static final int READ_BUFFER_SIZE = 0x100000;
private ByteBuffer d_readBuf = ByteBuffer.allocateDirect(READ_BUFFER_SIZE); // 1Mb
private final Thread d_thread = new Thread(this);
private SocketAddress d_address;
private Selector d_selector;
private SocketChannel d_channel;
public TimeClient() {
try {
d_address = new InetSocketAddress(InetAddress.getLocalHost(), 38900);
}
catch(Exception e) {
e.printStackTrace();
}
}
public void start() throws IOException {
System.out.println( " starting event loop " );
d_thread.start();
}
public void stop() throws IOException, InterruptedException {
System.out.println( " stopping event loop " );
d_thread.interrupt();
d_selector.wakeup();
d_thread.join();
}
public void run() {
System.out.println( " event loop running " );
try {
d_selector = Selector.open();
d_channel = SocketChannel.open();
d_channel.configureBlocking(false);
d_channel.socket().setTcpNoDelay(true); // disable nagle
if (d_channel.connect(d_address)) {
processConnect(true);
}
d_channel.register(d_selector, SelectionKey.OP_CONNECT);
while(!d_thread.isInterrupted() && d_channel.isOpen()) {
int numSelectedKeys = d_selector.select();
System.out.println( " Spinning ? " );
if (numSelectedKeys > 0) {
processSelectedKeys(d_selector.selectedKeys());
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
d_channel.close();
d_selector.close();
}
catch (IOException e) {
System.out.println(e);
}
System.out.println( " connection closed " );
}
System.out.println( " event loop terminated " );
}
private void processSelectedKeys(Set<SelectionKey> keys) throws Exception {
Iterator<SelectionKey> itr = keys.iterator();
while (itr.hasNext()) {
SelectionKey key = itr.next();
if (key.isReadable()) {
processRead(key);
}
else if (key.isConnectable()) {
processConnect(false);
}
else {
System.out.println( " Un-interested key selected " );
}
itr.remove();
}
}
private void processConnect(boolean connectComplete) throws Exception {
SelectionKey key = d_channel.keyFor(d_selector);
if (connectComplete) {
System.out.println( " ConnectComplete Immediately " + connectComplete);
d_channel.register(d_selector, SelectionKey.OP_READ);
}
else if (d_channel.finishConnect()) {
System.out.println( " connected to " + d_address);
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
}
}
private void processRead(SelectionKey key) throws Exception {
ReadableByteChannel ch = (ReadableByteChannel)key.channel();
int bytesOp = 0, bytesTotal = 0;
while (d_readBuf.hasRemaining() && (bytesOp = ch.read(d_readBuf)) > 0) {
bytesTotal += bytesOp;
}
if (bytesTotal > 0) {
d_readBuf.flip();
// do something with data
d_readBuf.compact();
}
else if (bytesOp == -1) {
System.out.println( " peer closed read channel " );
ch.close();
}
}
public static void main(String[] args) throws Exception {
final TimeClient client = new TimeClient();
try {
client.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// TimeServer.java Sample implementation [Just used for connecting]
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.net.*;
import java.util.*;
// Listen on a port for connections and write back the current time.
public class TimeServer {
private static final int DEFAULT_TIME_PORT = 38900;
// Constructor with no arguments creates a time server on default port.
public TimeServer() throws Exception {
acceptConnections(this.DEFAULT_TIME_PORT);
}
// Constructor with port argument creates a time server on specified port.
public TimeServer(int port) throws Exception {
acceptConnections(port);
}
// Accept connections for current time. Lazy Exception thrown.
private static void acceptConnections(int port) throws Exception {
// Selector for incoming time requests
Selector acceptSelector = SelectorProvider.provider().openSelector();
System.out.println( " Coming here 1 " );
// Create a new server socket and set to non blocking mode
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
// Bind the server socket to the local host and port
// Bind the server socket to the local host and port
InetAddress lh = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(lh, port);
ssc.socket().bind(isa);
System.out.println( " Coming here 2 " + isa);
// Register accepts on the server socket with the selector. This
// step tells the selector that the socket wants to be put on the
// ready list when accept operations occur, so allowing multiplexed
// non-blocking I/O to take place.
SelectionKey acceptKey = ssc.register(acceptSelector,
SelectionKey.OP_ACCEPT);
int keysAdded = 0;
System.out.println( " Coming here 3 " );
// Here's where everything happens. The select method will
// return when any operations registered above have occurred, the
// thread has been interrupted, etc.
while ((keysAdded = acceptSelector.select()) > 0) {
// Someone is ready for I/O, get the ready keys
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
System.out.println( " Coming here 4 " );
// Walk through the ready keys collection and process date requests.
while (i.hasNext()) {
SelectionKey sk = (SelectionKey)i.next();
i.remove();
// The key indexes into the selector so you
// can retrieve the socket that's ready for I/O
ServerSocketChannel nextReady =
(ServerSocketChannel)sk.channel();
// Accept the date request and send back the date string
Socket s = nextReady.accept().socket();
// Write the current time to the socket
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
Date now = new Date();
out.println(now);
out.close();
}
}
}
// Entry point.
public static void main(String[] args) {
try {
TimeServer t = new TimeServer();
} catch(Exception e) {
e.printStackTrace();
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Destroying the current selector and creating a new one (and registering OP_CONNECT) fixes the problem
java version " 1.7.0_09 "
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) Server VM (build 23.5-b02, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
SunOS sundev31 5.10 144488-05 sun4v sparc SUNW,T5240
A DESCRIPTION OF THE PROBLEM :
It seems there was a previous issue with epoll spinning, for which the suggested workaround was to thrash the sector and create a new one and fix was available for Java 7. Is the fix available for all platforms ?
REGRESSION. Last worked in version 6u31
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a selector
2. Register OP_CONNECT, if AsyncSocket.connect() returned false
3. After connection gets created, unregister OP_CONNECT
4. Register OP_READ
5. The selector starts spining
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
// TimeClient.java [Connects and registers OP_READ]
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class TimeClient implements Runnable {
private static final int READ_BUFFER_SIZE = 0x100000;
private ByteBuffer d_readBuf = ByteBuffer.allocateDirect(READ_BUFFER_SIZE); // 1Mb
private final Thread d_thread = new Thread(this);
private SocketAddress d_address;
private Selector d_selector;
private SocketChannel d_channel;
public TimeClient() {
try {
d_address = new InetSocketAddress(InetAddress.getLocalHost(), 38900);
}
catch(Exception e) {
e.printStackTrace();
}
}
public void start() throws IOException {
System.out.println( " starting event loop " );
d_thread.start();
}
public void stop() throws IOException, InterruptedException {
System.out.println( " stopping event loop " );
d_thread.interrupt();
d_selector.wakeup();
d_thread.join();
}
public void run() {
System.out.println( " event loop running " );
try {
d_selector = Selector.open();
d_channel = SocketChannel.open();
d_channel.configureBlocking(false);
d_channel.socket().setTcpNoDelay(true); // disable nagle
if (d_channel.connect(d_address)) {
processConnect(true);
}
d_channel.register(d_selector, SelectionKey.OP_CONNECT);
while(!d_thread.isInterrupted() && d_channel.isOpen()) {
int numSelectedKeys = d_selector.select();
System.out.println( " Spinning ? " );
if (numSelectedKeys > 0) {
processSelectedKeys(d_selector.selectedKeys());
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
d_channel.close();
d_selector.close();
}
catch (IOException e) {
System.out.println(e);
}
System.out.println( " connection closed " );
}
System.out.println( " event loop terminated " );
}
private void processSelectedKeys(Set<SelectionKey> keys) throws Exception {
Iterator<SelectionKey> itr = keys.iterator();
while (itr.hasNext()) {
SelectionKey key = itr.next();
if (key.isReadable()) {
processRead(key);
}
else if (key.isConnectable()) {
processConnect(false);
}
else {
System.out.println( " Un-interested key selected " );
}
itr.remove();
}
}
private void processConnect(boolean connectComplete) throws Exception {
SelectionKey key = d_channel.keyFor(d_selector);
if (connectComplete) {
System.out.println( " ConnectComplete Immediately " + connectComplete);
d_channel.register(d_selector, SelectionKey.OP_READ);
}
else if (d_channel.finishConnect()) {
System.out.println( " connected to " + d_address);
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
}
}
private void processRead(SelectionKey key) throws Exception {
ReadableByteChannel ch = (ReadableByteChannel)key.channel();
int bytesOp = 0, bytesTotal = 0;
while (d_readBuf.hasRemaining() && (bytesOp = ch.read(d_readBuf)) > 0) {
bytesTotal += bytesOp;
}
if (bytesTotal > 0) {
d_readBuf.flip();
// do something with data
d_readBuf.compact();
}
else if (bytesOp == -1) {
System.out.println( " peer closed read channel " );
ch.close();
}
}
public static void main(String[] args) throws Exception {
final TimeClient client = new TimeClient();
try {
client.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// TimeServer.java Sample implementation [Just used for connecting]
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.net.*;
import java.util.*;
// Listen on a port for connections and write back the current time.
public class TimeServer {
private static final int DEFAULT_TIME_PORT = 38900;
// Constructor with no arguments creates a time server on default port.
public TimeServer() throws Exception {
acceptConnections(this.DEFAULT_TIME_PORT);
}
// Constructor with port argument creates a time server on specified port.
public TimeServer(int port) throws Exception {
acceptConnections(port);
}
// Accept connections for current time. Lazy Exception thrown.
private static void acceptConnections(int port) throws Exception {
// Selector for incoming time requests
Selector acceptSelector = SelectorProvider.provider().openSelector();
System.out.println( " Coming here 1 " );
// Create a new server socket and set to non blocking mode
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
// Bind the server socket to the local host and port
// Bind the server socket to the local host and port
InetAddress lh = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(lh, port);
ssc.socket().bind(isa);
System.out.println( " Coming here 2 " + isa);
// Register accepts on the server socket with the selector. This
// step tells the selector that the socket wants to be put on the
// ready list when accept operations occur, so allowing multiplexed
// non-blocking I/O to take place.
SelectionKey acceptKey = ssc.register(acceptSelector,
SelectionKey.OP_ACCEPT);
int keysAdded = 0;
System.out.println( " Coming here 3 " );
// Here's where everything happens. The select method will
// return when any operations registered above have occurred, the
// thread has been interrupted, etc.
while ((keysAdded = acceptSelector.select()) > 0) {
// Someone is ready for I/O, get the ready keys
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
System.out.println( " Coming here 4 " );
// Walk through the ready keys collection and process date requests.
while (i.hasNext()) {
SelectionKey sk = (SelectionKey)i.next();
i.remove();
// The key indexes into the selector so you
// can retrieve the socket that's ready for I/O
ServerSocketChannel nextReady =
(ServerSocketChannel)sk.channel();
// Accept the date request and send back the date string
Socket s = nextReady.accept().socket();
// Write the current time to the socket
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
Date now = new Date();
out.println(now);
out.close();
}
}
}
// Entry point.
public static void main(String[] args) {
try {
TimeServer t = new TimeServer();
} catch(Exception e) {
e.printStackTrace();
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Destroying the current selector and creating a new one (and registering OP_CONNECT) fixes the problem