diff --git a/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java b/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java index 5f25d944d6..dfeaba0984 100644 --- a/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java +++ b/src/main/java/org/jboss/netty/channel/socket/nio/NioSocketChannel.java @@ -59,8 +59,20 @@ abstract class NioSocketChannel extends AbstractChannel final Runnable writeTask = new WriteTask(); final AtomicInteger writeBufferSize = new AtomicInteger(); final Queue writeBuffer = new WriteBuffer(); - boolean wasWritable; - boolean mightNeedToNotifyUnwritability; + + /** Previous return value of isWritable() */ + boolean oldWritable; + /** + * Set to true if the amount of data in the writeBuffer exceeds + * the high water mark, as specified in NioSocketChannelConfig. + */ + boolean exceededHighWaterMark; + /** + * true if and only if NioWorker is firing an event which might cause + * infinite recursion. + */ + boolean firingEvent; + MessageEvent currentWriteEvent; int currentWriteIndex; @@ -160,7 +172,7 @@ abstract class NioSocketChannel extends AbstractChannel int newWriteBufferSize = writeBufferSize.addAndGet( -((ChannelBuffer) e.getMessage()).readableBytes()); if (newWriteBufferSize <= getConfig().getWriteBufferLowWaterMark()) { - mightNeedToNotifyUnwritability = true; + exceededHighWaterMark = true; } } return e; diff --git a/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java b/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java index 7791ed4ac9..7601f54917 100644 --- a/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java +++ b/src/main/java/org/jboss/netty/channel/socket/nio/NioWorker.java @@ -479,25 +479,35 @@ class NioWorker implements Runnable { private static void fireChannelInterestChangedIfNecessary( NioSocketChannel channel, boolean open) { - int interestOps = channel.getRawInterestOps(); - boolean wasWritable = channel.wasWritable; - boolean writable = channel.wasWritable = open? channel.isWritable() : false; - if (wasWritable) { - if (writable) { - if (channel.mightNeedToNotifyUnwritability) { - channel.mightNeedToNotifyUnwritability = false; + if (channel.firingEvent) { + // Prevent StackOverflowError. + return; + } + + channel.firingEvent = true; + try { + int interestOps = channel.getRawInterestOps(); + boolean wasWritable = channel.oldWritable; + boolean writable = channel.oldWritable = open? channel.isWritable() : false; + if (wasWritable) { + if (writable) { + if (channel.exceededHighWaterMark) { + channel.exceededHighWaterMark = false; + fireChannelInterestChanged(channel, interestOps | Channel.OP_WRITE); + fireChannelInterestChanged(channel, interestOps & ~Channel.OP_WRITE); + } + } else { fireChannelInterestChanged(channel, interestOps | Channel.OP_WRITE); - fireChannelInterestChanged(channel, interestOps & ~Channel.OP_WRITE); } } else { - fireChannelInterestChanged(channel, interestOps | Channel.OP_WRITE); - } - } else { - if (writable) { - fireChannelInterestChanged(channel, interestOps & ~Channel.OP_WRITE); - } else { - fireChannelInterestChanged(channel, interestOps | Channel.OP_WRITE); + if (writable) { + fireChannelInterestChanged(channel, interestOps & ~Channel.OP_WRITE); + } else { + fireChannelInterestChanged(channel, interestOps | Channel.OP_WRITE); + } } + } finally { + channel.firingEvent = false; } }