Rewrite bridge implementation in DefaultChannelHandlerContext
This commit splits bridge into two parts. One is NextBridgeFeeder, which provides ByteBuf and MessageBuf that are local to the context whose next*Buffer() has been invoked on. The other is a thread-safe queue that stores the data fed by NextBridgeFeeder.feed(). By splitting the bridge into the two parts, the data pushed by a handler is not lost anymore when the next handler who provided the next buffer is removed from the pipeline. - Fixes #1272
This commit is contained in:
parent
5bfb408b7d
commit
4a792151b0
@ -67,28 +67,26 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
// Note we use an AtomicReferenceFieldUpdater for atomic operations on these to save memory. This will save us
|
// Note we use an AtomicReferenceFieldUpdater for atomic operations on these to save memory. This will save us
|
||||||
// 64 bytes per Bridge.
|
// 64 bytes per Bridge.
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private volatile MessageBridge inMsgBridge;
|
private volatile Queue<Object> inBridge;
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private volatile MessageBridge outMsgBridge;
|
private volatile Queue<Object> outBridge;
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private volatile ByteBridge inByteBridge;
|
private volatile NextBridgeFeeder nextInBridgeFeeder;
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private volatile ByteBridge outByteBridge;
|
private volatile NextBridgeFeeder nextOutBridgeFeeder;
|
||||||
|
|
||||||
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, MessageBridge> IN_MSG_BRIDGE_UPDATER
|
@SuppressWarnings("rawtypes")
|
||||||
= AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class,
|
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, Queue> IN_BRIDGE_UPDATER =
|
||||||
MessageBridge.class, "inMsgBridge");
|
AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class, Queue.class, "inBridge");
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, MessageBridge> OUT_MSG_BRIDGE_UPDATER
|
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, Queue> OUT_BRIDGE_UPDATER =
|
||||||
= AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class,
|
AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class, Queue.class, "outBridge");
|
||||||
MessageBridge.class, "outMsgBridge");
|
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, NextBridgeFeeder>
|
||||||
|
NEXT_IN_BRIDGE_FEEDER = AtomicReferenceFieldUpdater.newUpdater(
|
||||||
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, ByteBridge> IN_BYTE_BRIDGE_UPDATER
|
DefaultChannelHandlerContext.class, NextBridgeFeeder.class, "nextInBridgeFeeder");
|
||||||
= AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class,
|
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, NextBridgeFeeder>
|
||||||
ByteBridge.class, "inByteBridge");
|
NEXT_OUT_BRIDGE_FEEDER = AtomicReferenceFieldUpdater.newUpdater(
|
||||||
private static final AtomicReferenceFieldUpdater<DefaultChannelHandlerContext, ByteBridge> OUT_BYTE_BRIDGE_UPDATER
|
DefaultChannelHandlerContext.class, NextBridgeFeeder.class, "nextOutBridgeFeeder");
|
||||||
= AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class,
|
|
||||||
ByteBridge.class, "outByteBridge");
|
|
||||||
|
|
||||||
// Lazily instantiated tasks used to trigger events to a handler with different executor.
|
// Lazily instantiated tasks used to trigger events to a handler with different executor.
|
||||||
private Runnable invokeInboundBufferUpdatedTask;
|
private Runnable invokeInboundBufferUpdatedTask;
|
||||||
@ -315,72 +313,82 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
outMsgBuf = h.msgSink;
|
outMsgBuf = h.msgSink;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillInboundBridge() {
|
|
||||||
if (!(handler instanceof ChannelInboundHandler)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inMsgBridge != null) {
|
|
||||||
MessageBridge bridge = inMsgBridge;
|
|
||||||
if (bridge != null) {
|
|
||||||
bridge.fill();
|
|
||||||
}
|
|
||||||
} else if (inByteBridge != null) {
|
|
||||||
ByteBridge bridge = inByteBridge;
|
|
||||||
if (bridge != null) {
|
|
||||||
bridge.fill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillOutboundBridge() {
|
|
||||||
if (!(handler instanceof ChannelOutboundHandler)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outMsgBridge != null) {
|
|
||||||
MessageBridge bridge = outMsgBridge;
|
|
||||||
if (bridge != null) {
|
|
||||||
bridge.fill();
|
|
||||||
}
|
|
||||||
} else if (outByteBridge != null) {
|
|
||||||
ByteBridge bridge = outByteBridge;
|
|
||||||
if (bridge != null) {
|
|
||||||
bridge.fill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean flushInboundBridge() {
|
private boolean flushInboundBridge() {
|
||||||
if (inMsgBridge != null) {
|
Queue<Object> inBridge = this.inBridge;
|
||||||
MessageBridge bridge = inMsgBridge;
|
if (inBridge == null) {
|
||||||
if (bridge != null) {
|
|
||||||
return bridge.flush(inMsgBuf);
|
|
||||||
}
|
|
||||||
} else if (inByteBridge != null) {
|
|
||||||
ByteBridge bridge = inByteBridge;
|
|
||||||
if (bridge != null) {
|
|
||||||
return bridge.flush(inByteBuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return flushBridge(inBridge, inMsgBuf, inByteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean flushOutboundBridge() {
|
private boolean flushOutboundBridge() {
|
||||||
if (outMsgBridge != null) {
|
Queue<Object> outBridge = this.outBridge;
|
||||||
MessageBridge bridge = outMsgBridge;
|
if (outBridge == null) {
|
||||||
if (bridge != null) {
|
return true;
|
||||||
return bridge.flush(outMsgBuf);
|
}
|
||||||
|
return flushBridge(outBridge, outMsgBuf, outByteBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean flushBridge(Queue<Object> bridge, MessageBuf<Object> msgBuf, ByteBuf byteBuf) {
|
||||||
|
if (bridge == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean nextBufferHadEnoughRoom = true;
|
||||||
|
for (;;) {
|
||||||
|
Object o = bridge.peek();
|
||||||
|
if (o == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (o instanceof Object[]) {
|
||||||
|
Object[] data = (Object[]) o;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < data.length; i ++) {
|
||||||
|
Object m = data[i];
|
||||||
|
if (m == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgBuf.offer(m)) {
|
||||||
|
data[i] = null;
|
||||||
|
} else {
|
||||||
|
System.arraycopy(data, i, data, 0, data.length - i);
|
||||||
|
for (int j = i + 1; j < data.length; j ++) {
|
||||||
|
data[j] = null;
|
||||||
|
}
|
||||||
|
nextBufferHadEnoughRoom = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (o instanceof ByteBuf) {
|
||||||
|
ByteBuf data = (ByteBuf) o;
|
||||||
|
if (byteBuf.writerIndex() > byteBuf.maxCapacity() - data.readableBytes()) {
|
||||||
|
// The target buffer is not going to be able to accept all data in the bridge.
|
||||||
|
byteBuf.capacity(byteBuf.maxCapacity());
|
||||||
|
byteBuf.writeBytes(data, byteBuf.writableBytes());
|
||||||
|
nextBufferHadEnoughRoom = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
byteBuf.writeBytes(data);
|
||||||
|
} finally {
|
||||||
|
data.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (nextBufferHadEnoughRoom) {
|
||||||
|
Object removed = bridge.remove();
|
||||||
|
assert removed == o;
|
||||||
}
|
}
|
||||||
} else if (outByteBridge != null) {
|
|
||||||
ByteBridge bridge = outByteBridge;
|
|
||||||
if (bridge != null) {
|
|
||||||
return bridge.flush(outByteBuf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return nextBufferHadEnoughRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRemoved() {
|
void setRemoved() {
|
||||||
@ -398,13 +406,12 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
|
|
||||||
final ChannelHandler handler = handler();
|
final ChannelHandler handler = handler();
|
||||||
|
|
||||||
|
try {
|
||||||
if (handler instanceof ChannelInboundHandler) {
|
if (handler instanceof ChannelInboundHandler) {
|
||||||
try {
|
try {
|
||||||
((ChannelInboundHandler) handler).freeInboundBuffer(this);
|
((ChannelInboundHandler) handler).freeInboundBuffer(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
notifyHandlerException(e);
|
notifyHandlerException(e);
|
||||||
} finally {
|
|
||||||
freeInboundBridge();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (handler instanceof ChannelOutboundHandler) {
|
if (handler instanceof ChannelOutboundHandler) {
|
||||||
@ -412,34 +419,52 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
((ChannelOutboundHandler) handler).freeOutboundBuffer(this);
|
((ChannelOutboundHandler) handler).freeOutboundBuffer(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
notifyHandlerException(e);
|
notifyHandlerException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
freeOutboundBridge();
|
free();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void freeInboundBridge() {
|
private void free() {
|
||||||
ByteBridge inByteBridge = this.inByteBridge;
|
freeInbound();
|
||||||
if (inByteBridge != null) {
|
freeOutbound();
|
||||||
inByteBridge.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBridge inMsgBridge = this.inMsgBridge;
|
private void freeInbound() {
|
||||||
if (inMsgBridge != null) {
|
// Release the bridge feeder
|
||||||
inMsgBridge.release();
|
NextBridgeFeeder feeder;
|
||||||
|
feeder = nextInBridgeFeeder;
|
||||||
|
if (feeder != null) {
|
||||||
|
feeder.release();
|
||||||
|
nextInBridgeFeeder = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn if the bridge has unflushed elements.
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
Queue<Object> bridge;
|
||||||
|
bridge = inBridge;
|
||||||
|
if (bridge != null && !bridge.isEmpty()) {
|
||||||
|
logger.warn("inbound bridge not empty - bug?: {}", bridge.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void freeOutboundBridge() {
|
private void freeOutbound() {
|
||||||
ByteBridge outByteBridge = this.outByteBridge;
|
// Release the bridge feeder
|
||||||
if (outByteBridge != null) {
|
NextBridgeFeeder feeder = nextOutBridgeFeeder;
|
||||||
outByteBridge.release();
|
if (feeder != null) {
|
||||||
|
feeder.release();
|
||||||
|
nextOutBridgeFeeder = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageBridge outMsgBridge = this.outMsgBridge;
|
// Warn if the bridge has unflushed elements.
|
||||||
if (outMsgBridge != null) {
|
if (logger.isWarnEnabled()) {
|
||||||
outMsgBridge.release();
|
Queue<Object> bridge = outBridge;
|
||||||
|
if (bridge != null && !bridge.isEmpty()) {
|
||||||
|
logger.warn("outbound bridge not empty - bug?: {}", bridge.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,17 +574,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
return ctx.inByteBuf;
|
return ctx.inByteBuf;
|
||||||
}
|
}
|
||||||
if (executor().inEventLoop(currentThread)) {
|
if (executor().inEventLoop(currentThread)) {
|
||||||
ByteBridge bridge = ctx.inByteBridge;
|
return nextInBridgeFeeder().byteBuf;
|
||||||
if (bridge == null) {
|
|
||||||
bridge = new ByteBridge(ctx, true);
|
|
||||||
if (!IN_BYTE_BRIDGE_UPDATER.compareAndSet(ctx, null, bridge)) {
|
|
||||||
// release it as it was set before
|
|
||||||
bridge.release();
|
|
||||||
|
|
||||||
bridge = ctx.inByteBridge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bridge.byteBuf;
|
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("nextInboundByteBuffer() called from outside the eventLoop");
|
throw new IllegalStateException("nextInboundByteBuffer() called from outside the eventLoop");
|
||||||
}
|
}
|
||||||
@ -577,17 +592,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
return ctx.inMsgBuf;
|
return ctx.inMsgBuf;
|
||||||
}
|
}
|
||||||
if (executor().inEventLoop(currentThread)) {
|
if (executor().inEventLoop(currentThread)) {
|
||||||
MessageBridge bridge = ctx.inMsgBridge;
|
return nextInBridgeFeeder().msgBuf;
|
||||||
if (bridge == null) {
|
|
||||||
bridge = new MessageBridge();
|
|
||||||
if (!IN_MSG_BRIDGE_UPDATER.compareAndSet(ctx, null, bridge)) {
|
|
||||||
// release it as it was set before
|
|
||||||
bridge.release();
|
|
||||||
|
|
||||||
bridge = ctx.inMsgBridge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bridge.msgBuf;
|
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("nextInboundMessageBuffer() called from outside the eventLoop");
|
throw new IllegalStateException("nextInboundMessageBuffer() called from outside the eventLoop");
|
||||||
}
|
}
|
||||||
@ -595,6 +600,18 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NextBridgeFeeder nextInBridgeFeeder() {
|
||||||
|
NextBridgeFeeder feeder = nextInBridgeFeeder;
|
||||||
|
if (feeder == null) {
|
||||||
|
feeder = new NextInboundBridgeFeeder();
|
||||||
|
if (!NEXT_IN_BRIDGE_FEEDER.compareAndSet(this, null, feeder)) {
|
||||||
|
feeder.release();
|
||||||
|
feeder = nextInBridgeFeeder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return feeder;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf nextOutboundByteBuffer() {
|
public ByteBuf nextOutboundByteBuffer() {
|
||||||
DefaultChannelHandlerContext ctx = prev;
|
DefaultChannelHandlerContext ctx = prev;
|
||||||
@ -605,17 +622,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
return ctx.outboundByteBuffer();
|
return ctx.outboundByteBuffer();
|
||||||
}
|
}
|
||||||
if (executor().inEventLoop(currentThread)) {
|
if (executor().inEventLoop(currentThread)) {
|
||||||
ByteBridge bridge = ctx.outByteBridge;
|
return nextOutBridgeFeeder().byteBuf;
|
||||||
if (bridge == null) {
|
|
||||||
bridge = new ByteBridge(ctx, false);
|
|
||||||
if (!OUT_BYTE_BRIDGE_UPDATER.compareAndSet(ctx, null, bridge)) {
|
|
||||||
// release it as it was set before
|
|
||||||
bridge.release();
|
|
||||||
|
|
||||||
bridge = ctx.outByteBridge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bridge.byteBuf;
|
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("nextOutboundByteBuffer() called from outside the eventLoop");
|
throw new IllegalStateException("nextOutboundByteBuffer() called from outside the eventLoop");
|
||||||
}
|
}
|
||||||
@ -633,17 +640,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
return ctx.outboundMessageBuffer();
|
return ctx.outboundMessageBuffer();
|
||||||
}
|
}
|
||||||
if (executor().inEventLoop(currentThread)) {
|
if (executor().inEventLoop(currentThread)) {
|
||||||
MessageBridge bridge = ctx.outMsgBridge;
|
return nextOutBridgeFeeder().msgBuf;
|
||||||
if (bridge == null) {
|
|
||||||
bridge = new MessageBridge();
|
|
||||||
if (!OUT_MSG_BRIDGE_UPDATER.compareAndSet(ctx, null, bridge)) {
|
|
||||||
// release it as it was set before
|
|
||||||
bridge.release();
|
|
||||||
|
|
||||||
bridge = ctx.outMsgBridge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bridge.msgBuf;
|
|
||||||
}
|
}
|
||||||
throw new IllegalStateException("nextOutboundMessageBuffer() called from outside the eventLoop");
|
throw new IllegalStateException("nextOutboundMessageBuffer() called from outside the eventLoop");
|
||||||
}
|
}
|
||||||
@ -651,6 +648,18 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NextBridgeFeeder nextOutBridgeFeeder() {
|
||||||
|
NextBridgeFeeder feeder = nextOutBridgeFeeder;
|
||||||
|
if (feeder == null) {
|
||||||
|
feeder = new NextOutboundBridgeFeeder();
|
||||||
|
if (!NEXT_OUT_BRIDGE_FEEDER.compareAndSet(this, null, feeder)) {
|
||||||
|
feeder.release();
|
||||||
|
feeder = nextOutBridgeFeeder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return feeder;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelHandlerContext fireChannelRegistered() {
|
public ChannelHandlerContext fireChannelRegistered() {
|
||||||
final DefaultChannelHandlerContext next = findContextInbound();
|
final DefaultChannelHandlerContext next = findContextInbound();
|
||||||
@ -858,7 +867,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
|
|
||||||
private void fireInboundBufferUpdated0(final DefaultChannelHandlerContext next) {
|
private void fireInboundBufferUpdated0(final DefaultChannelHandlerContext next) {
|
||||||
if (!pipeline.isInboundShutdown()) {
|
if (!pipeline.isInboundShutdown()) {
|
||||||
next.fillInboundBridge();
|
feedNextInBridge();
|
||||||
// This comparison is safe because this method is always executed from the executor.
|
// This comparison is safe because this method is always executed from the executor.
|
||||||
if (next.executor == executor) {
|
if (next.executor == executor) {
|
||||||
next.invokeInboundBufferUpdated();
|
next.invokeInboundBufferUpdated();
|
||||||
@ -886,6 +895,13 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void feedNextInBridge() {
|
||||||
|
NextBridgeFeeder feeder = nextInBridgeFeeder;
|
||||||
|
if (feeder != null) {
|
||||||
|
feeder.feed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void invokeInboundBufferUpdated() {
|
private void invokeInboundBufferUpdated() {
|
||||||
ChannelStateHandler handler = (ChannelStateHandler) handler();
|
ChannelStateHandler handler = (ChannelStateHandler) handler();
|
||||||
|
|
||||||
@ -1234,10 +1250,17 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
"Unable to flush as outbound buffer of next handler was freed already"));
|
"Unable to flush as outbound buffer of next handler was freed already"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prev.fillOutboundBridge();
|
feedNextOutBridge();
|
||||||
prev.invokeFlush(promise, currentThread);
|
prev.invokeFlush(promise, currentThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void feedNextOutBridge() {
|
||||||
|
NextBridgeFeeder feeder = nextOutBridgeFeeder;
|
||||||
|
if (feeder != null) {
|
||||||
|
feeder.feed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ChannelFuture invokeFlush(final ChannelPromise promise, Thread currentThread) {
|
private ChannelFuture invokeFlush(final ChannelPromise promise, Thread currentThread) {
|
||||||
EventExecutor executor = executor();
|
EventExecutor executor = executor();
|
||||||
if (executor.inEventLoop(currentThread)) {
|
if (executor.inEventLoop(currentThread)) {
|
||||||
@ -1431,7 +1454,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
notifyHandlerException(t);
|
notifyHandlerException(t);
|
||||||
} finally {
|
} finally {
|
||||||
freeInboundBridge();
|
freeInbound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1483,7 +1506,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
notifyHandlerException(t);
|
notifyHandlerException(t);
|
||||||
} finally {
|
} finally {
|
||||||
freeOutboundBridge();
|
freeOutbound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1608,120 +1631,124 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class MessageBridge {
|
private abstract class NextBridgeFeeder {
|
||||||
private final MessageBuf<Object> msgBuf = Unpooled.messageBuffer();
|
final MessageBuf<Object> msgBuf;
|
||||||
|
final ByteBuf byteBuf;
|
||||||
|
|
||||||
private final Queue<Object[]> exchangeBuf = new ConcurrentLinkedQueue<Object[]>();
|
protected NextBridgeFeeder() {
|
||||||
|
msgBuf = Unpooled.messageBuffer();
|
||||||
private void fill() {
|
byteBuf = ChannelHandlerUtil.allocate(DefaultChannelHandlerContext.this);
|
||||||
if (msgBuf.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Object[] data = msgBuf.toArray();
|
|
||||||
msgBuf.clear();
|
|
||||||
exchangeBuf.add(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean flush(MessageBuf<Object> out) {
|
|
||||||
for (;;) {
|
|
||||||
Object[] data = exchangeBuf.peek();
|
|
||||||
if (data == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < data.length; i ++) {
|
|
||||||
Object m = data[i];
|
|
||||||
if (m == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out.offer(m)) {
|
|
||||||
data[i] = null;
|
|
||||||
} else {
|
|
||||||
System.arraycopy(data, i, data, 0, data.length - i);
|
|
||||||
for (int j = i + 1; j < data.length; j ++) {
|
|
||||||
data[j] = null;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exchangeBuf.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void release() {
|
|
||||||
msgBuf.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ByteBridge {
|
|
||||||
private final ByteBuf byteBuf;
|
|
||||||
|
|
||||||
private final Queue<ByteBuf> exchangeBuf = new ConcurrentLinkedQueue<ByteBuf>();
|
|
||||||
private final ChannelHandlerContext ctx;
|
|
||||||
|
|
||||||
ByteBridge(ChannelHandlerContext ctx, boolean inbound) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
if (inbound) {
|
|
||||||
if (ctx.inboundByteBuffer().isDirect()) {
|
|
||||||
byteBuf = ctx.alloc().directBuffer();
|
|
||||||
} else {
|
|
||||||
byteBuf = ctx.alloc().heapBuffer();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (ctx.outboundByteBuffer().isDirect()) {
|
|
||||||
byteBuf = ctx.alloc().directBuffer();
|
|
||||||
} else {
|
|
||||||
byteBuf = ctx.alloc().heapBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fill() {
|
|
||||||
if (!byteBuf.isReadable()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final void feed() {
|
||||||
int dataLen = byteBuf.readableBytes();
|
int dataLen = byteBuf.readableBytes();
|
||||||
|
if (dataLen != 0) {
|
||||||
ByteBuf data;
|
ByteBuf data;
|
||||||
if (byteBuf.isDirect()) {
|
if (byteBuf.isDirect()) {
|
||||||
data = ctx.alloc().directBuffer(dataLen, dataLen);
|
data = alloc().directBuffer(dataLen, dataLen);
|
||||||
} else {
|
} else {
|
||||||
data = ctx.alloc().buffer(dataLen, dataLen);
|
data = alloc().heapBuffer(dataLen, dataLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
byteBuf.readBytes(data).discardSomeReadBytes();
|
byteBuf.readBytes(data).discardSomeReadBytes();
|
||||||
|
nextByteBridge().add(data);
|
||||||
exchangeBuf.add(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean flush(ByteBuf out) {
|
if (!msgBuf.isEmpty()) {
|
||||||
for (;;) {
|
Object[] data = msgBuf.toArray();
|
||||||
ByteBuf data = exchangeBuf.peek();
|
msgBuf.clear();
|
||||||
if (data == null) {
|
nextMessageBridge().add(data);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out.writerIndex() > out.maxCapacity() - data.readableBytes()) {
|
|
||||||
// The target buffer is not going to be able to accept all data in the bridge.
|
|
||||||
out.capacity(out.maxCapacity());
|
|
||||||
out.writeBytes(data, out.writableBytes());
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
exchangeBuf.remove();
|
|
||||||
try {
|
|
||||||
out.writeBytes(data);
|
|
||||||
} finally {
|
|
||||||
data.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void release() {
|
final void release() {
|
||||||
byteBuf.release();
|
byteBuf.release();
|
||||||
|
msgBuf.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Queue<Object> nextByteBridge();
|
||||||
|
protected abstract Queue<Object> nextMessageBridge();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class NextInboundBridgeFeeder extends NextBridgeFeeder {
|
||||||
|
@Override
|
||||||
|
protected Queue<Object> nextByteBridge() {
|
||||||
|
DefaultChannelHandlerContext ctx = next;
|
||||||
|
for (;;) {
|
||||||
|
if (ctx.hasInboundByteBuffer()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctx = ctx.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Queue<Object> nextMessageBridge() {
|
||||||
|
DefaultChannelHandlerContext ctx = next;
|
||||||
|
for (;;) {
|
||||||
|
if (ctx.hasInboundMessageBuffer()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctx = ctx.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Queue<Object> bridge(DefaultChannelHandlerContext ctx) {
|
||||||
|
Queue<Object> bridge = ctx.inBridge;
|
||||||
|
if (bridge == null) {
|
||||||
|
Queue<Object> newBridge = new ConcurrentLinkedQueue<Object>();
|
||||||
|
if (IN_BRIDGE_UPDATER.compareAndSet(ctx, null, newBridge)) {
|
||||||
|
bridge = newBridge;
|
||||||
|
} else {
|
||||||
|
bridge = ctx.inBridge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bridge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class NextOutboundBridgeFeeder extends NextBridgeFeeder {
|
||||||
|
@Override
|
||||||
|
protected Queue<Object> nextByteBridge() {
|
||||||
|
DefaultChannelHandlerContext ctx = prev;
|
||||||
|
for (;;) {
|
||||||
|
if (ctx.hasOutboundByteBuffer()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctx = ctx.prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Queue<Object> nextMessageBridge() {
|
||||||
|
DefaultChannelHandlerContext ctx = prev;
|
||||||
|
for (;;) {
|
||||||
|
if (ctx.hasOutboundMessageBuffer()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ctx = ctx.prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Queue<Object> bridge(DefaultChannelHandlerContext ctx) {
|
||||||
|
Queue<Object> bridge = ctx.outBridge;
|
||||||
|
if (bridge == null) {
|
||||||
|
Queue<Object> newBridge = new ConcurrentLinkedQueue<Object>();
|
||||||
|
if (OUT_BRIDGE_UPDATER.compareAndSet(ctx, null, newBridge)) {
|
||||||
|
bridge = newBridge;
|
||||||
|
} else {
|
||||||
|
bridge = ctx.outBridge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bridge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public class LocalTransportThreadModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout = 30000)
|
@Test(timeout = 30000)
|
||||||
@Ignore
|
@Ignore("regression test")
|
||||||
public void testStagedExecutionMultiple() throws Throwable {
|
public void testStagedExecutionMultiple() throws Throwable {
|
||||||
for (int i = 0; i < 10; i ++) {
|
for (int i = 0; i < 10; i ++) {
|
||||||
testStagedExecution();
|
testStagedExecution();
|
||||||
@ -206,7 +206,7 @@ public class LocalTransportThreadModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout = 30000)
|
@Test(timeout = 30000)
|
||||||
@Ignore
|
@Ignore("regression test")
|
||||||
public void testConcurrentMessageBufferAccess() throws Throwable {
|
public void testConcurrentMessageBufferAccess() throws Throwable {
|
||||||
EventLoopGroup l = new LocalEventLoopGroup(4, new PrefixThreadFactory("l"));
|
EventLoopGroup l = new LocalEventLoopGroup(4, new PrefixThreadFactory("l"));
|
||||||
EventExecutorGroup e1 = new DefaultEventExecutorGroup(4, new PrefixThreadFactory("e1"));
|
EventExecutorGroup e1 = new DefaultEventExecutorGroup(4, new PrefixThreadFactory("e1"));
|
||||||
|
Loading…
Reference in New Issue
Block a user