Limit future notification stack depth / Robost writeCounter management
- Also ported the discard example while testing this commit
This commit is contained in:
parent
a2698e65fb
commit
e48281471b
@ -15,15 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.discard;
|
package io.netty.example.discard;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import io.netty.channel.ChannelBootstrap;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
|
|
||||||
import io.netty.bootstrap.ClientBootstrap;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipelineFactory;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.channel.Channels;
|
import io.netty.channel.socket.nio.NioEventLoop;
|
||||||
import io.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keeps sending random data to the specified address.
|
* Keeps sending random data to the specified address.
|
||||||
@ -40,28 +37,28 @@ public class DiscardClient {
|
|||||||
this.firstMessageSize = firstMessageSize;
|
this.firstMessageSize = firstMessageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() throws Exception {
|
||||||
// Configure the client.
|
ChannelBootstrap b = new ChannelBootstrap();
|
||||||
ClientBootstrap bootstrap = new ClientBootstrap(
|
try {
|
||||||
new NioClientSocketChannelFactory(
|
b.eventLoop(new NioEventLoop())
|
||||||
Executors.newCachedThreadPool()));
|
.channel(new NioSocketChannel())
|
||||||
|
.remoteAddress(host, port)
|
||||||
|
.initializer(new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
ch.pipeline().addLast(new DiscardClientHandler(firstMessageSize));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Set up the pipeline factory.
|
// Make the connection attempt.
|
||||||
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
|
ChannelFuture f = b.connect().sync();
|
||||||
public ChannelPipeline getPipeline() throws Exception {
|
|
||||||
return Channels.pipeline(
|
|
||||||
new DiscardClientHandler(firstMessageSize));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start the connection attempt.
|
// Wait until the connection is closed.
|
||||||
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
|
f.channel().closeFuture().sync();
|
||||||
|
|
||||||
// Wait until the connection is closed or the connection attempt fails.
|
} finally {
|
||||||
future.channel().getCloseFuture().awaitUninterruptibly();
|
b.shutdown();
|
||||||
|
}
|
||||||
// Shut down thread pools to exit.
|
|
||||||
bootstrap.releaseExternalResources();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -15,31 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.discard;
|
package io.netty.example.discard;
|
||||||
|
|
||||||
|
import io.netty.buffer.ChannelBuffer;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelFutureListener;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundStreamHandlerAdapter;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
|
||||||
import io.netty.buffer.ChannelBuffers;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelState;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.ExceptionEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.channel.SimpleChannelUpstreamHandler;
|
|
||||||
import io.netty.channel.WriteCompletionEvent;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a client-side channel.
|
* Handles a client-side channel.
|
||||||
*/
|
*/
|
||||||
public class DiscardClientHandler extends SimpleChannelUpstreamHandler {
|
public class DiscardClientHandler extends ChannelInboundStreamHandlerAdapter {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(
|
private static final Logger logger = Logger.getLogger(
|
||||||
DiscardClientHandler.class.getName());
|
DiscardClientHandler.class.getName());
|
||||||
|
|
||||||
private long transferredBytes;
|
|
||||||
private final byte[] content;
|
private final byte[] content;
|
||||||
|
private ChannelInboundHandlerContext<Byte> ctx;
|
||||||
|
private ChannelBuffer out;
|
||||||
|
|
||||||
public DiscardClientHandler(int messageSize) {
|
public DiscardClientHandler(int messageSize) {
|
||||||
if (messageSize <= 0) {
|
if (messageSize <= 0) {
|
||||||
@ -49,70 +44,55 @@ public class DiscardClientHandler extends SimpleChannelUpstreamHandler {
|
|||||||
content = new byte[messageSize];
|
content = new byte[messageSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTransferredBytes() {
|
|
||||||
return transferredBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
|
public void channelActive(ChannelInboundHandlerContext<Byte> ctx)
|
||||||
if (e instanceof ChannelStateEvent) {
|
throws Exception {
|
||||||
if (((ChannelStateEvent) e).getState() != ChannelState.INTEREST_OPS) {
|
this.ctx = ctx;
|
||||||
logger.info(e.toString());
|
out = ctx.out().byteBuffer();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let SimpleChannelHandler call actual event handler methods below.
|
|
||||||
super.handleUpstream(ctx, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
|
|
||||||
// Send the initial messages.
|
// Send the initial messages.
|
||||||
generateTraffic(e);
|
generateTraffic();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) {
|
|
||||||
// Keep sending messages whenever the current socket buffer has room.
|
|
||||||
generateTraffic(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
|
public void inboundBufferUpdated(ChannelInboundHandlerContext<Byte> ctx)
|
||||||
|
throws Exception {
|
||||||
// Server is supposed to send nothing. Therefore, do nothing.
|
// Server is supposed to send nothing. Therefore, do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) {
|
|
||||||
transferredBytes += e.getWrittenAmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
|
public void exceptionCaught(ChannelInboundHandlerContext<Byte> ctx,
|
||||||
|
Throwable cause) throws Exception {
|
||||||
// Close the connection when an exception is raised.
|
// Close the connection when an exception is raised.
|
||||||
logger.log(
|
logger.log(
|
||||||
Level.WARNING,
|
Level.WARNING,
|
||||||
"Unexpected exception from downstream.",
|
"Unexpected exception from downstream.",
|
||||||
e.cause());
|
cause);
|
||||||
e.channel().close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateTraffic(ChannelStateEvent e) {
|
long counter;
|
||||||
// Keep generating traffic until the channel is unwritable.
|
|
||||||
// A channel becomes unwritable when its internal buffer is full.
|
private void generateTraffic() {
|
||||||
// If you keep writing messages ignoring this property,
|
// Fill the outbound buffer up to 64KiB
|
||||||
// you will end up with an OutOfMemoryError.
|
while (out.readableBytes() < 65536) {
|
||||||
Channel channel = e.channel();
|
out.writeBytes(content);
|
||||||
while (channel.isWritable()) {
|
|
||||||
ChannelBuffer m = nextMessage();
|
|
||||||
if (m == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
channel.write(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush the outbound buffer to the socket.
|
||||||
|
// Once flushed, generate the same amount of traffic again.
|
||||||
|
ctx.flush().addListener(GENERATE_TRAFFIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelBuffer nextMessage() {
|
private final ChannelFutureListener GENERATE_TRAFFIC = new ChannelFutureListener() {
|
||||||
return ChannelBuffers.wrappedBuffer(content);
|
@Override
|
||||||
}
|
public void operationComplete(ChannelFuture future) throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
out.clear();
|
||||||
|
generateTraffic();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.discard;
|
package io.netty.example.discard;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import io.netty.channel.ChannelFuture;
|
||||||
import java.util.concurrent.Executors;
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ServerChannelBootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.socket.nio.NioEventLoop;
|
||||||
import io.netty.channel.ChannelPipelineFactory;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
import io.netty.channel.Channels;
|
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discards any incoming data.
|
* Discards any incoming data.
|
||||||
@ -35,21 +33,29 @@ public class DiscardServer {
|
|||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() throws Exception {
|
||||||
// Configure the server.
|
ServerChannelBootstrap b = new ServerChannelBootstrap();
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap(
|
try {
|
||||||
new NioServerSocketChannelFactory(
|
b.eventLoop(new NioEventLoop(), new NioEventLoop())
|
||||||
Executors.newCachedThreadPool()));
|
.channel(new NioServerSocketChannel())
|
||||||
|
.localAddress(port)
|
||||||
|
.childInitializer(new ChannelInitializer<SocketChannel>() {
|
||||||
|
@Override
|
||||||
|
public void initChannel(SocketChannel ch) throws Exception {
|
||||||
|
ch.pipeline().addLast(new DiscardServerHandler());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Set up the pipeline factory.
|
// Bind and start to accept incoming connections.
|
||||||
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
|
ChannelFuture f = b.bind().sync();
|
||||||
public ChannelPipeline getPipeline() throws Exception {
|
|
||||||
return Channels.pipeline(new DiscardServerHandler());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Bind and start to accept incoming connections.
|
// Wait until the server socket is closed.
|
||||||
bootstrap.bind(new InetSocketAddress(port));
|
// In this example, this does not happen, but you can do that to gracefully
|
||||||
|
// shut down your server.
|
||||||
|
f.channel().closeFuture().sync();
|
||||||
|
} finally {
|
||||||
|
b.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
@ -15,54 +15,37 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.example.discard;
|
package io.netty.example.discard;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelInboundHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundStreamHandlerAdapter;
|
||||||
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.ExceptionEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.channel.SimpleChannelUpstreamHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a server-side channel.
|
* Handles a server-side channel.
|
||||||
*/
|
*/
|
||||||
public class DiscardServerHandler extends SimpleChannelUpstreamHandler {
|
public class DiscardServerHandler extends ChannelInboundStreamHandlerAdapter {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(
|
private static final Logger logger = Logger.getLogger(
|
||||||
DiscardServerHandler.class.getName());
|
DiscardServerHandler.class.getName());
|
||||||
|
|
||||||
private long transferredBytes;
|
|
||||||
|
|
||||||
public long getTransferredBytes() {
|
|
||||||
return transferredBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
|
public void inboundBufferUpdated(ChannelInboundHandlerContext<Byte> ctx)
|
||||||
if (e instanceof ChannelStateEvent) {
|
throws Exception {
|
||||||
logger.info(e.toString());
|
// Discard the received data silently.
|
||||||
}
|
ctx.in().byteBuffer().clear();
|
||||||
|
|
||||||
// Let SimpleChannelHandler call actual event handler methods below.
|
|
||||||
super.handleUpstream(ctx, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
|
|
||||||
// Discard received data silently by doing nothing.
|
|
||||||
transferredBytes += ((ChannelBuffer) e.getMessage()).readableBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
|
public void exceptionCaught(ChannelInboundHandlerContext<Byte> ctx,
|
||||||
|
Throwable cause) throws Exception {
|
||||||
// Close the connection when an exception is raised.
|
// Close the connection when an exception is raised.
|
||||||
logger.log(
|
logger.log(
|
||||||
Level.WARNING,
|
Level.WARNING,
|
||||||
"Unexpected exception from downstream.",
|
"Unexpected exception from downstream.",
|
||||||
e.cause());
|
cause);
|
||||||
e.channel().close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,9 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
|
|
||||||
private ClosedChannelException closedChannelException;
|
private ClosedChannelException closedChannelException;
|
||||||
private final Deque<FlushCheckpoint> flushCheckpoints = new ArrayDeque<FlushCheckpoint>();
|
private final Deque<FlushCheckpoint> flushCheckpoints = new ArrayDeque<FlushCheckpoint>();
|
||||||
protected long writeCounter;
|
private long writeCounter;
|
||||||
|
private boolean inFlushNow;
|
||||||
|
private boolean flushNowPending;
|
||||||
|
|
||||||
/** Cache for the string representation of this channel */
|
/** Cache for the string representation of this channel */
|
||||||
private boolean strValActive;
|
private boolean strValActive;
|
||||||
@ -362,6 +364,8 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
|
|
||||||
protected abstract class AbstractUnsafe implements Unsafe {
|
protected abstract class AbstractUnsafe implements Unsafe {
|
||||||
|
|
||||||
|
private final Runnable flushLaterTask = new FlushLater();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelBufferHolder<Object> out() {
|
public ChannelBufferHolder<Object> out() {
|
||||||
return firstOut();
|
return firstOut();
|
||||||
@ -554,19 +558,26 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
// Attempt/perform outbound I/O if:
|
// Attempt/perform outbound I/O if:
|
||||||
// - the channel is inactive - flush0() will fail the futures.
|
// - the channel is inactive - flush0() will fail the futures.
|
||||||
// - the event loop has no plan to call flushForcibly().
|
// - the event loop has no plan to call flushForcibly().
|
||||||
try {
|
if (!inFlushNow) {
|
||||||
if (!isActive() || !isFlushPending()) {
|
try {
|
||||||
doFlush(out());
|
if (!isActive() || !isFlushPending()) {
|
||||||
|
flushNow();
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
notifyFlushFutures(t);
|
||||||
|
pipeline().fireExceptionCaught(t);
|
||||||
|
if (t instanceof IOException) {
|
||||||
|
close(voidFuture());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (!isActive()) {
|
||||||
|
close(unsafe().voidFuture());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} else {
|
||||||
notifyFlushFutures(t);
|
if (!flushNowPending) {
|
||||||
pipeline().fireExceptionCaught(t);
|
flushNowPending = true;
|
||||||
if (t instanceof IOException) {
|
eventLoop().execute(flushLaterTask);
|
||||||
close(voidFuture());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (!isActive()) {
|
|
||||||
close(unsafe().voidFuture());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -581,18 +592,38 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flushNow() {
|
public void flushNow() {
|
||||||
|
if (inFlushNow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inFlushNow = true;
|
||||||
try {
|
try {
|
||||||
doFlush(out());
|
Throwable cause = null;
|
||||||
} catch (Throwable t) {
|
ChannelBufferHolder<Object> out = out();
|
||||||
notifyFlushFutures(t);
|
int oldSize = out.size();
|
||||||
pipeline().fireExceptionCaught(t);
|
try {
|
||||||
if (t instanceof IOException) {
|
doFlush(out);
|
||||||
close(voidFuture());
|
} catch (Throwable t) {
|
||||||
|
cause = t;
|
||||||
|
} finally {
|
||||||
|
writeCounter += oldSize - out.size();
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
|
if (cause == null) {
|
||||||
|
notifyFlushFutures();
|
||||||
|
} else {
|
||||||
|
notifyFlushFutures(cause);
|
||||||
|
pipeline().fireExceptionCaught(cause);
|
||||||
|
if (cause instanceof IOException) {
|
||||||
|
close(voidFuture());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isActive()) {
|
if (!isActive()) {
|
||||||
close(unsafe().voidFuture());
|
close(unsafe().voidFuture());
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
inFlushNow = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,6 +646,14 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FlushLater implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
flushNowPending = false;
|
||||||
|
unsafe().flush(voidFuture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract boolean isCompatible(EventLoop loop);
|
protected abstract boolean isCompatible(EventLoop loop);
|
||||||
|
|
||||||
protected abstract ChannelBufferHolder<Object> firstOut();
|
protected abstract ChannelBufferHolder<Object> firstOut();
|
||||||
@ -631,38 +670,46 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
|
|
||||||
protected abstract boolean isFlushPending();
|
protected abstract boolean isFlushPending();
|
||||||
|
|
||||||
protected void notifyFlushFutures() {
|
private void notifyFlushFutures() {
|
||||||
if (flushCheckpoints.isEmpty()) {
|
if (flushCheckpoints.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final long flushedAmount = AbstractChannel.this.writeCounter;
|
final long writeCounter = AbstractChannel.this.writeCounter;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
FlushCheckpoint cp = flushCheckpoints.poll();
|
FlushCheckpoint cp = flushCheckpoints.peek();
|
||||||
if (cp == null) {
|
if (cp == null) {
|
||||||
|
// Reset the counter if there's nothing in the notification list.
|
||||||
|
AbstractChannel.this.writeCounter = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (cp.flushCheckpoint() > flushedAmount) {
|
|
||||||
|
if (cp.flushCheckpoint() > writeCounter) {
|
||||||
|
if (writeCounter > 0 && flushCheckpoints.size() == 1) {
|
||||||
|
AbstractChannel.this.writeCounter = 0;
|
||||||
|
cp.flushCheckpoint(cp.flushCheckpoint() - writeCounter);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flushCheckpoints.remove();
|
||||||
cp.future().setSuccess();
|
cp.future().setSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid overflow
|
// Avoid overflow
|
||||||
if (flushCheckpoints.isEmpty()) {
|
final long newWriteCounter = AbstractChannel.this.writeCounter;
|
||||||
// Reset the counter if there's nothing in the notification list.
|
if (newWriteCounter >= 0x1000000000000000L) {
|
||||||
AbstractChannel.this.writeCounter = 0;
|
// Reset the counter only when the counter grew pretty large
|
||||||
} else if (flushedAmount >= 0x1000000000000000L) {
|
|
||||||
// Otherwise, reset the counter only when the counter grew pretty large
|
|
||||||
// so that we can reduce the cost of updating all entries in the notification list.
|
// so that we can reduce the cost of updating all entries in the notification list.
|
||||||
AbstractChannel.this.writeCounter = 0;
|
AbstractChannel.this.writeCounter = 0;
|
||||||
for (FlushCheckpoint cp: flushCheckpoints) {
|
for (FlushCheckpoint cp: flushCheckpoints) {
|
||||||
cp.flushCheckpoint(cp.flushCheckpoint() - flushedAmount);
|
cp.flushCheckpoint(cp.flushCheckpoint() - newWriteCounter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyFlushFutures(Throwable cause) {
|
private void notifyFlushFutures(Throwable cause) {
|
||||||
|
notifyFlushFutures();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
FlushCheckpoint cp = flushCheckpoints.poll();
|
FlushCheckpoint cp = flushCheckpoints.poll();
|
||||||
if (cp == null) {
|
if (cp == null) {
|
||||||
|
@ -15,9 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.channel;
|
package io.netty.channel;
|
||||||
|
|
||||||
import io.netty.logging.InternalLogger;
|
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,9 +23,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
public abstract class CompleteChannelFuture implements ChannelFuture {
|
public abstract class CompleteChannelFuture implements ChannelFuture {
|
||||||
|
|
||||||
private static final InternalLogger logger =
|
|
||||||
InternalLoggerFactory.getInstance(CompleteChannelFuture.class);
|
|
||||||
|
|
||||||
private final Channel channel;
|
private final Channel channel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,31 +39,13 @@ public abstract class CompleteChannelFuture implements ChannelFuture {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture addListener(final ChannelFutureListener listener) {
|
public ChannelFuture addListener(final ChannelFutureListener listener) {
|
||||||
if (channel().eventLoop().inEventLoop()) {
|
if (listener == null) {
|
||||||
notifyListener(listener);
|
throw new NullPointerException("listener");
|
||||||
} else {
|
|
||||||
channel().eventLoop().execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
notifyListener(listener);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
DefaultChannelFuture.notifyListener(this, listener);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyListener(ChannelFutureListener listener) {
|
|
||||||
try {
|
|
||||||
listener.operationComplete(this);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn(
|
|
||||||
"An exception was thrown by " +
|
|
||||||
ChannelFutureListener.class.getSimpleName() + ".", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture removeListener(ChannelFutureListener listener) {
|
public ChannelFuture removeListener(ChannelFutureListener listener) {
|
||||||
// NOOP
|
// NOOP
|
||||||
|
@ -38,6 +38,14 @@ public class DefaultChannelFuture extends FlushCheckpoint implements ChannelFutu
|
|||||||
private static final InternalLogger logger =
|
private static final InternalLogger logger =
|
||||||
InternalLoggerFactory.getInstance(DefaultChannelFuture.class);
|
InternalLoggerFactory.getInstance(DefaultChannelFuture.class);
|
||||||
|
|
||||||
|
private static final int MAX_LISTENER_STACK_DEPTH = 8;
|
||||||
|
private static final ThreadLocal<Integer> LISTENER_STACK_DEPTH = new ThreadLocal<Integer>() {
|
||||||
|
@Override
|
||||||
|
protected Integer initialValue() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private static final Throwable CANCELLED = new Throwable();
|
private static final Throwable CANCELLED = new Throwable();
|
||||||
|
|
||||||
private static volatile boolean useDeadLockChecker = true;
|
private static volatile boolean useDeadLockChecker = true;
|
||||||
@ -154,17 +162,7 @@ public class DefaultChannelFuture extends FlushCheckpoint implements ChannelFutu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (notifyNow) {
|
if (notifyNow) {
|
||||||
if (channel().eventLoop().inEventLoop()) {
|
notifyListener(this, listener);
|
||||||
notifyListener(listener);
|
|
||||||
} else {
|
|
||||||
channel().eventLoop().execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
notifyListener(listener);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -433,12 +431,12 @@ public class DefaultChannelFuture extends FlushCheckpoint implements ChannelFutu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (channel().eventLoop().inEventLoop()) {
|
if (channel().eventLoop().inEventLoop()) {
|
||||||
notifyListener(firstListener);
|
notifyListener0(this, firstListener);
|
||||||
firstListener = null;
|
firstListener = null;
|
||||||
|
|
||||||
if (otherListeners != null) {
|
if (otherListeners != null) {
|
||||||
for (ChannelFutureListener l: otherListeners) {
|
for (ChannelFutureListener l: otherListeners) {
|
||||||
notifyListener(l);
|
notifyListener0(this, l);
|
||||||
}
|
}
|
||||||
otherListeners = null;
|
otherListeners = null;
|
||||||
}
|
}
|
||||||
@ -450,10 +448,10 @@ public class DefaultChannelFuture extends FlushCheckpoint implements ChannelFutu
|
|||||||
channel().eventLoop().execute(new Runnable() {
|
channel().eventLoop().execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
notifyListener(firstListener);
|
notifyListener0(DefaultChannelFuture.this, firstListener);
|
||||||
if (otherListeners != null) {
|
if (otherListeners != null) {
|
||||||
for (ChannelFutureListener l: otherListeners) {
|
for (ChannelFutureListener l: otherListeners) {
|
||||||
notifyListener(l);
|
notifyListener0(DefaultChannelFuture.this, l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,9 +459,33 @@ public class DefaultChannelFuture extends FlushCheckpoint implements ChannelFutu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyListener(ChannelFutureListener l) {
|
static void notifyListener(final ChannelFuture f, final ChannelFutureListener l) {
|
||||||
|
EventLoop loop = f.channel().eventLoop();
|
||||||
|
if (loop.inEventLoop()) {
|
||||||
|
final Integer stackDepth = LISTENER_STACK_DEPTH.get();
|
||||||
|
if (stackDepth < MAX_LISTENER_STACK_DEPTH) {
|
||||||
|
LISTENER_STACK_DEPTH.set(stackDepth + 1);
|
||||||
|
try {
|
||||||
|
notifyListener0(f, l);
|
||||||
|
} finally {
|
||||||
|
LISTENER_STACK_DEPTH.set(stackDepth);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
notifyListener(f, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void notifyListener0(ChannelFuture f, ChannelFutureListener l) {
|
||||||
try {
|
try {
|
||||||
l.operationComplete(this);
|
l.operationComplete(f);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
if (logger.isWarnEnabled()) {
|
if (logger.isWarnEnabled()) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
|
@ -109,46 +109,65 @@ public abstract class SingleThreadEventLoop extends AbstractExecutorService impl
|
|||||||
|
|
||||||
protected Runnable pollTask() {
|
protected Runnable pollTask() {
|
||||||
assert inEventLoop();
|
assert inEventLoop();
|
||||||
|
|
||||||
Runnable task = taskQueue.poll();
|
Runnable task = taskQueue.poll();
|
||||||
if (task == null) {
|
if (task != null) {
|
||||||
if (fetchScheduledTasks()) {
|
return task;
|
||||||
task = taskQueue.poll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return task;
|
|
||||||
|
if (fetchScheduledTasks()) {
|
||||||
|
task = taskQueue.poll();
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Runnable takeTask() throws InterruptedException {
|
protected Runnable takeTask() throws InterruptedException {
|
||||||
assert inEventLoop();
|
assert inEventLoop();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Runnable task = taskQueue.poll(SCHEDULE_CHECK_INTERVAL * 2 / 3, TimeUnit.NANOSECONDS);
|
Runnable task = taskQueue.poll(SCHEDULE_CHECK_INTERVAL * 2 / 3, TimeUnit.NANOSECONDS);
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
fetchScheduledTasks();
|
fetchScheduledTasks();
|
||||||
|
task = taskQueue.poll();
|
||||||
|
if (task != null) {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Runnable peekTask() {
|
protected Runnable peekTask() {
|
||||||
assert inEventLoop();
|
assert inEventLoop();
|
||||||
|
|
||||||
Runnable task = taskQueue.peek();
|
Runnable task = taskQueue.peek();
|
||||||
if (task == null) {
|
if (task != null) {
|
||||||
if (fetchScheduledTasks()) {
|
return task;
|
||||||
task = taskQueue.peek();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return task;
|
|
||||||
|
if (fetchScheduledTasks()) {
|
||||||
|
task = taskQueue.peek();
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean hasTasks() {
|
protected boolean hasTasks() {
|
||||||
assert inEventLoop();
|
assert inEventLoop();
|
||||||
|
|
||||||
boolean empty = taskQueue.isEmpty();
|
boolean empty = taskQueue.isEmpty();
|
||||||
if (empty) {
|
if (!empty) {
|
||||||
if (fetchScheduledTasks()) {
|
return true;
|
||||||
empty = taskQueue.isEmpty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return !empty;
|
|
||||||
|
if (fetchScheduledTasks()) {
|
||||||
|
return !taskQueue.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addTask(Runnable task) {
|
protected void addTask(Runnable task) {
|
||||||
|
@ -82,9 +82,7 @@ abstract class AbstractNioMessageChannel extends AbstractNioChannel {
|
|||||||
for (int i = writeSpinCount; i >= 0; i --) {
|
for (int i = writeSpinCount; i >= 0; i --) {
|
||||||
int localFlushedAmount = doWriteMessages(buf, i == 0);
|
int localFlushedAmount = doWriteMessages(buf, i == 0);
|
||||||
if (localFlushedAmount > 0) {
|
if (localFlushedAmount > 0) {
|
||||||
writeCounter += localFlushedAmount;
|
|
||||||
wrote = true;
|
wrote = true;
|
||||||
notifyFlushFutures();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,8 +89,6 @@ abstract class AbstractNioStreamChannel extends AbstractNioChannel {
|
|||||||
for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
|
for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {
|
||||||
int localFlushedAmount = doWriteBytes(buf, i == 0);
|
int localFlushedAmount = doWriteBytes(buf, i == 0);
|
||||||
if (localFlushedAmount > 0) {
|
if (localFlushedAmount > 0) {
|
||||||
writeCounter += localFlushedAmount;
|
|
||||||
notifyFlushFutures();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!buf.readable()) {
|
if (!buf.readable()) {
|
||||||
|
@ -70,11 +70,7 @@ abstract class AbstractOioMessageChannel extends AbstractOioChannel {
|
|||||||
|
|
||||||
private void flushMessageBuf(Queue<Object> buf) throws Exception {
|
private void flushMessageBuf(Queue<Object> buf) throws Exception {
|
||||||
while (!buf.isEmpty()) {
|
while (!buf.isEmpty()) {
|
||||||
int localFlushedAmount = doWriteMessages(buf);
|
doWriteMessages(buf);
|
||||||
if (localFlushedAmount > 0) {
|
|
||||||
writeCounter += localFlushedAmount;
|
|
||||||
notifyFlushFutures();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,11 +72,7 @@ abstract class AbstractOioStreamChannel extends AbstractOioChannel {
|
|||||||
|
|
||||||
private void flushByteBuf(ChannelBuffer buf) throws Exception {
|
private void flushByteBuf(ChannelBuffer buf) throws Exception {
|
||||||
while (buf.readable()) {
|
while (buf.readable()) {
|
||||||
int localFlushedAmount = doWriteBytes(buf);
|
doWriteBytes(buf);
|
||||||
if (localFlushedAmount > 0) {
|
|
||||||
writeCounter += localFlushedAmount;
|
|
||||||
notifyFlushFutures();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
buf.clear();
|
buf.clear();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user