Simplified DefaultChannelPipeline by making its list head final

- Previously, head was a volatile field which is null at the beginning.
  While iterating over the pipeline, if the loop hits null, it called
  Channel.Unsafe explicitly.
- Instead, I created an outbound handler that redirects all requests
  to the unsafe and made it a final field of the pipeline.
- As a result, DefaultChannelPipeline code became much simpler.
This commit is contained in:
Trustin Lee 2012-06-03 18:51:42 -07:00
parent f6e14b636f
commit f3734e1eb9
3 changed files with 313 additions and 254 deletions

View File

@ -72,7 +72,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
private final Channel parent; private final Channel parent;
private final Integer id; private final Integer id;
private final Unsafe unsafe; private final Unsafe unsafe;
private final ChannelPipeline pipeline; private final DefaultChannelPipeline pipeline;
private final ChannelFuture succeededFuture = new SucceededChannelFuture(this); private final ChannelFuture succeededFuture = new SucceededChannelFuture(this);
private final ChannelFuture voidFuture = new VoidChannelFuture(this); private final ChannelFuture voidFuture = new VoidChannelFuture(this);
private final CloseFuture closeFuture = new CloseFuture(this); private final CloseFuture closeFuture = new CloseFuture(this);
@ -82,7 +82,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
private volatile EventLoop eventLoop; private volatile EventLoop eventLoop;
private volatile boolean registered; private volatile boolean registered;
private final ChannelBufferHolder<Object> directOutbound;
private ClosedChannelException closedChannelException; private ClosedChannelException closedChannelException;
private final Deque<FlushCheckpoint> flushCheckpoints = new ArrayDeque<FlushCheckpoint>(); private final Deque<FlushCheckpoint> flushCheckpoints = new ArrayDeque<FlushCheckpoint>();
private long writeCounter; private long writeCounter;
@ -123,7 +122,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
this.parent = parent; this.parent = parent;
this.id = id; this.id = id;
unsafe = newUnsafe(); unsafe = newUnsafe();
directOutbound = (ChannelBufferHolder<Object>) outboundBuffer; pipeline = new DefaultChannelPipeline(this);
closeFuture().addListener(new ChannelFutureListener() { closeFuture().addListener(new ChannelFutureListener() {
@Override @Override
@ -132,7 +131,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
} }
}); });
pipeline = new DefaultChannelPipeline(this);
} }
@Override @Override
@ -385,7 +383,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
@Override @Override
public final ChannelBufferHolder<Object> directOutbound() { public final ChannelBufferHolder<Object> directOutbound() {
return directOutbound; return pipeline.directOutbound;
} }
@Override @Override
@ -628,7 +626,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
} }
inFlushNow = true; inFlushNow = true;
final ChannelBufferHolder<Object> out = directOutbound; final ChannelBufferHolder<Object> out = directOutbound();
try { try {
Throwable cause = null; Throwable cause = null;
int oldSize = out.size(); int oldSize = out.size();

View File

@ -5,6 +5,8 @@ import io.netty.util.DefaultAttributeMap;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelInboundHandlerContext<Object>, ChannelOutboundHandlerContext<Object> { final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelInboundHandlerContext<Object>, ChannelOutboundHandlerContext<Object> {
volatile DefaultChannelHandlerContext next; volatile DefaultChannelHandlerContext next;
@ -17,7 +19,17 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
private final boolean canHandleInbound; private final boolean canHandleInbound;
private final boolean canHandleOutbound; private final boolean canHandleOutbound;
final ChannelBufferHolder<Object> in; final ChannelBufferHolder<Object> in;
private final ChannelBufferHolder<Object> out; final ChannelBufferHolder<Object> out;
// When the two handlers run in a different thread and they are next to each other,
// each other's buffers can be accessed at the same time resuslting in a race condition.
// To avoid such situation, we lazily creates an additional thread-safe buffer called
// 'bridge' so that the two handlers access each other's buffer only via the bridges.
// The content written into a bridge is flushed into the actual buffer by flushBridge().
final AtomicReference<BlockingQueue<Object>> inMsgBridge;
final AtomicReference<BlockingQueue<Object>> outMsgBridge;
final AtomicReference<ChannelBuffer> inByteBridge;
final AtomicReference<ChannelBuffer> outByteBridge;
// Runnables that calls handlers // Runnables that calls handlers
final Runnable fireChannelRegisteredTask = new Runnable() { final Runnable fireChannelRegisteredTask = new Runnable() {
@ -73,6 +85,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void run() { public void run() {
DefaultChannelHandlerContext ctx = DefaultChannelHandlerContext.this; DefaultChannelHandlerContext ctx = DefaultChannelHandlerContext.this;
flushBridge();
try { try {
((ChannelInboundHandler<Object>) ctx.handler()).inboundBufferUpdated(ctx); ((ChannelInboundHandler<Object>) ctx.handler()).inboundBufferUpdated(ctx);
} catch (Throwable t) { } catch (Throwable t) {
@ -135,8 +148,23 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
} catch (Exception e) { } catch (Exception e) {
throw new ChannelPipelineException("A user handler failed to create a new inbound buffer.", e); throw new ChannelPipelineException("A user handler failed to create a new inbound buffer.", e);
} }
if (!in.isBypass()) {
if (in.hasByteBuffer()) {
inByteBridge = new AtomicReference<ChannelBuffer>();
inMsgBridge = null;
} else {
inByteBridge = null;
inMsgBridge = new AtomicReference<BlockingQueue<Object>>();
}
} else {
inByteBridge = null;
inMsgBridge = null;
}
} else { } else {
in = null; in = null;
inByteBridge = null;
inMsgBridge = null;
} }
if (canHandleOutbound) { if (canHandleOutbound) {
try { try {
@ -148,8 +176,38 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
// TODO Release the inbound buffer once pooling is implemented. // TODO Release the inbound buffer once pooling is implemented.
} }
} }
if (!out.isBypass()) {
if (out.hasByteBuffer()) {
outByteBridge = new AtomicReference<ChannelBuffer>();
outMsgBridge = null;
} else {
outByteBridge = null;
outMsgBridge = new AtomicReference<BlockingQueue<Object>>();
}
} else {
outByteBridge = null;
outMsgBridge = null;
}
} else { } else {
out = null; out = null;
outByteBridge = null;
outMsgBridge = null;
}
}
void flushBridge() {
if (inMsgBridge != null) {
BlockingQueue<Object> bridge = inMsgBridge.get();
if (bridge != null) {
bridge.drainTo(in.messageBuffer());
}
}
if (outMsgBridge != null) {
BlockingQueue<Object> bridge = outMsgBridge.get();
if (bridge != null) {
bridge.drainTo(out.messageBuffer());
}
} }
} }
@ -229,7 +287,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
@Override @Override
public Queue<Object> nextInboundMessageBuffer() { public Queue<Object> nextInboundMessageBuffer() {
return DefaultChannelPipeline.nextInboundMessageBuffer(next); return DefaultChannelPipeline.nextInboundMessageBuffer(executor(), next);
} }
@Override @Override
@ -239,7 +297,7 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements
@Override @Override
public Queue<Object> nextOutboundMessageBuffer() { public Queue<Object> nextOutboundMessageBuffer() {
return pipeline.nextOutboundMessageBuffer(prev); return pipeline.nextOutboundMessageBuffer(executor(), prev);
} }
@Override @Override

View File

@ -18,6 +18,7 @@ package io.netty.channel;
import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffer;
import io.netty.logging.InternalLogger; import io.netty.logging.InternalLogger;
import io.netty.logging.InternalLoggerFactory; import io.netty.logging.InternalLoggerFactory;
import io.netty.util.internal.QueueFactory;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.ArrayList; import java.util.ArrayList;
@ -28,6 +29,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
/** /**
* The default {@link ChannelPipeline} implementation. It is usually created * The default {@link ChannelPipeline} implementation. It is usually created
@ -39,9 +43,9 @@ public class DefaultChannelPipeline implements ChannelPipeline {
final Channel channel; final Channel channel;
private final Channel.Unsafe unsafe; private final Channel.Unsafe unsafe;
private final ChannelBufferHolder<Object> directOutbound; final ChannelBufferHolder<Object> directOutbound;
private volatile DefaultChannelHandlerContext head; private final DefaultChannelHandlerContext head;
private volatile DefaultChannelHandlerContext tail; private volatile DefaultChannelHandlerContext tail;
private final Map<String, DefaultChannelHandlerContext> name2ctx = private final Map<String, DefaultChannelHandlerContext> name2ctx =
new HashMap<String, DefaultChannelHandlerContext>(4); new HashMap<String, DefaultChannelHandlerContext>(4);
@ -56,8 +60,14 @@ public class DefaultChannelPipeline implements ChannelPipeline {
throw new NullPointerException("channel"); throw new NullPointerException("channel");
} }
this.channel = channel; this.channel = channel;
HeadHandler headHandler = new HeadHandler();
head = new DefaultChannelHandlerContext(
this, null, null, null, generateName(headHandler), headHandler);
tail = head;
directOutbound = head.out;
unsafe = channel.unsafe(); unsafe = channel.unsafe();
directOutbound = unsafe.directOutbound();
} }
@Override @Override
@ -72,23 +82,20 @@ public class DefaultChannelPipeline implements ChannelPipeline {
@Override @Override
public synchronized ChannelPipeline addFirst(EventExecutor executor, String name, ChannelHandler handler) { public synchronized ChannelPipeline addFirst(EventExecutor executor, String name, ChannelHandler handler) {
if (name2ctx.isEmpty()) { checkDuplicateName(name);
init(executor, name, handler); DefaultChannelHandlerContext nextCtx = head.next;
} else { DefaultChannelHandlerContext newCtx =
checkDuplicateName(name); new DefaultChannelHandlerContext(this, executor, head, nextCtx, name, handler);
DefaultChannelHandlerContext oldHead = head;
DefaultChannelHandlerContext newHead =
new DefaultChannelHandlerContext(this, executor, null, oldHead, name, handler);
callBeforeAdd(newHead); callBeforeAdd(newCtx);
oldHead.prev = newHead; if (nextCtx != null) {
head = newHead; nextCtx.prev = newCtx;
name2ctx.put(name, newHead);
callAfterAdd(newHead);
} }
head.next = newCtx;
name2ctx.put(name, newCtx);
callAfterAdd(newCtx);
return this; return this;
} }
@ -99,23 +106,18 @@ public class DefaultChannelPipeline implements ChannelPipeline {
@Override @Override
public synchronized ChannelPipeline addLast(EventExecutor executor, String name, ChannelHandler handler) { public synchronized ChannelPipeline addLast(EventExecutor executor, String name, ChannelHandler handler) {
if (name2ctx.isEmpty()) { checkDuplicateName(name);
init(executor, name, handler); DefaultChannelHandlerContext oldTail = tail;
} else { DefaultChannelHandlerContext newTail =
checkDuplicateName(name); new DefaultChannelHandlerContext(this, executor, oldTail, null, name, handler);
DefaultChannelHandlerContext oldTail = tail;
DefaultChannelHandlerContext newTail =
new DefaultChannelHandlerContext(this, executor, oldTail, null, name, handler);
callBeforeAdd(newTail); callBeforeAdd(newTail);
oldTail.next = newTail; oldTail.next = newTail;
tail = newTail; tail = newTail;
name2ctx.put(name, newTail); name2ctx.put(name, newTail);
callAfterAdd(newTail);
}
callAfterAdd(newTail);
return this; return this;
} }
@ -127,22 +129,17 @@ public class DefaultChannelPipeline implements ChannelPipeline {
@Override @Override
public synchronized ChannelPipeline addBefore(EventExecutor executor, String baseName, String name, ChannelHandler handler) { public synchronized ChannelPipeline addBefore(EventExecutor executor, String baseName, String name, ChannelHandler handler) {
DefaultChannelHandlerContext ctx = getContextOrDie(baseName); DefaultChannelHandlerContext ctx = getContextOrDie(baseName);
if (ctx == head) { checkDuplicateName(name);
addFirst(name, handler); DefaultChannelHandlerContext newCtx =
} else { new DefaultChannelHandlerContext(this, executor, ctx.prev, ctx, name, handler);
checkDuplicateName(name);
DefaultChannelHandlerContext newCtx =
new DefaultChannelHandlerContext(this, executor, ctx.prev, ctx, name, handler);
callBeforeAdd(newCtx); callBeforeAdd(newCtx);
ctx.prev.next = newCtx; ctx.prev.next = newCtx;
ctx.prev = newCtx; ctx.prev = newCtx;
name2ctx.put(name, newCtx); name2ctx.put(name, newCtx);
callAfterAdd(newCtx);
}
callAfterAdd(newCtx);
return this; return this;
} }
@ -250,10 +247,9 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private DefaultChannelHandlerContext remove(DefaultChannelHandlerContext ctx) { private DefaultChannelHandlerContext remove(DefaultChannelHandlerContext ctx) {
if (head == tail) { if (head == tail) {
head = tail = null; return null;
name2ctx.clear();
} else if (ctx == head) { } else if (ctx == head) {
removeFirst(); throw new Error(); // Should never happen.
} else if (ctx == tail) { } else if (ctx == tail) {
removeLast(); removeLast();
} else { } else {
@ -272,55 +268,26 @@ public class DefaultChannelPipeline implements ChannelPipeline {
@Override @Override
public synchronized ChannelHandler removeFirst() { public synchronized ChannelHandler removeFirst() {
if (name2ctx.isEmpty()) { if (head == tail) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
return remove(head.next).handler();
DefaultChannelHandlerContext oldHead = head;
if (oldHead == null) {
throw new NoSuchElementException();
}
callBeforeRemove(oldHead);
if (oldHead.next == null) {
head = tail = null;
name2ctx.clear();
} else {
oldHead.next.prev = null;
head = oldHead.next;
name2ctx.remove(oldHead.name());
}
callAfterRemove(oldHead);
return oldHead.handler();
} }
@Override @Override
public synchronized ChannelHandler removeLast() { public synchronized ChannelHandler removeLast() {
if (name2ctx.isEmpty()) { if (head == tail) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
DefaultChannelHandlerContext oldTail = tail; DefaultChannelHandlerContext oldTail = tail;
if (oldTail == null) {
throw new NoSuchElementException();
}
callBeforeRemove(oldTail); callBeforeRemove(oldTail);
if (oldTail.prev == null) { oldTail.prev.next = null;
head = tail = null; tail = oldTail.prev;
name2ctx.clear(); name2ctx.remove(oldTail.name());
} else {
oldTail.prev.next = null;
tail = oldTail.prev;
name2ctx.remove(oldTail.name());
}
callBeforeRemove(oldTail); callBeforeRemove(oldTail);
return oldTail.handler(); return oldTail.handler();
} }
@ -343,8 +310,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private ChannelHandler replace(DefaultChannelHandlerContext ctx, String newName, ChannelHandler newHandler) { private ChannelHandler replace(DefaultChannelHandlerContext ctx, String newName, ChannelHandler newHandler) {
if (ctx == head) { if (ctx == head) {
removeFirst(); throw new IllegalArgumentException();
addFirst(newName, newHandler);
} else if (ctx == tail) { } else if (ctx == tail) {
removeLast(); removeLast();
addLast(newName, newHandler); addLast(newName, newHandler);
@ -472,20 +438,20 @@ public class DefaultChannelPipeline implements ChannelPipeline {
@Override @Override
public synchronized ChannelHandler first() { public synchronized ChannelHandler first() {
DefaultChannelHandlerContext head = this.head; DefaultChannelHandlerContext first = head.next;
if (head == null) { if (first == null) {
return null; return null;
} }
return head.handler(); return first.handler();
} }
@Override @Override
public synchronized ChannelHandler last() { public synchronized ChannelHandler last() {
DefaultChannelHandlerContext tail = this.tail; DefaultChannelHandlerContext last = tail;
if (tail == null) { if (last == head || last == null) {
return null; return null;
} }
return tail.handler(); return last.handler();
} }
@Override @Override
@ -548,7 +514,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
if (name2ctx.isEmpty()) { if (name2ctx.isEmpty()) {
return null; return null;
} }
DefaultChannelHandlerContext ctx = head; DefaultChannelHandlerContext ctx = head.next;
for (;;) { for (;;) {
if (handlerType.isAssignableFrom(ctx.handler().getClass())) { if (handlerType.isAssignableFrom(ctx.handler().getClass())) {
return ctx; return ctx;
@ -569,7 +535,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
return list; return list;
} }
DefaultChannelHandlerContext ctx = head; DefaultChannelHandlerContext ctx = head.next;
for (;;) { for (;;) {
list.add(ctx.name()); list.add(ctx.name());
ctx = ctx.next; ctx = ctx.next;
@ -587,7 +553,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
return map; return map;
} }
DefaultChannelHandlerContext ctx = head; DefaultChannelHandlerContext ctx = head.next;
for (;;) { for (;;) {
map.put(ctx.name(), ctx.handler()); map.put(ctx.name(), ctx.handler());
ctx = ctx.next; ctx = ctx.next;
@ -606,7 +572,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName()); buf.append(getClass().getSimpleName());
buf.append('{'); buf.append('{');
DefaultChannelHandlerContext ctx = head; DefaultChannelHandlerContext ctx = head.next;
for (;;) { for (;;) {
buf.append('('); buf.append('(');
buf.append(ctx.name()); buf.append(ctx.name());
@ -629,7 +595,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
throw new NoSuchBufferException( throw new NoSuchBufferException(
"The first inbound buffer of this channel must be a message buffer."); "The first inbound buffer of this channel must be a message buffer.");
} }
return nextInboundMessageBuffer(head); return nextInboundMessageBuffer(null, head.next);
} }
@Override @Override
@ -638,12 +604,12 @@ public class DefaultChannelPipeline implements ChannelPipeline {
throw new NoSuchBufferException( throw new NoSuchBufferException(
"The first inbound buffer of this channel must be a byte buffer."); "The first inbound buffer of this channel must be a byte buffer.");
} }
return nextInboundByteBuffer(head); return nextInboundByteBuffer(head.next);
} }
@Override @Override
public Queue<Object> outboundMessageBuffer() { public Queue<Object> outboundMessageBuffer() {
return nextOutboundMessageBuffer(tail); return nextOutboundMessageBuffer(null, tail);
} }
@Override @Override
@ -690,14 +656,27 @@ public class DefaultChannelPipeline implements ChannelPipeline {
} }
} }
static Queue<Object> nextInboundMessageBuffer(DefaultChannelHandlerContext ctx) { static Queue<Object> nextInboundMessageBuffer(
EventExecutor currentExecutor, DefaultChannelHandlerContext ctx) {
for (;;) { for (;;) {
if (ctx == null) { if (ctx == null) {
throw new NoSuchBufferException(); throw new NoSuchBufferException();
} }
ChannelBufferHolder<Object> in = ctx.inbound();
if (in != null && !in.isBypass() && in.hasMessageBuffer()) { final AtomicReference<BlockingQueue<Object>> inMsgBridge = ctx.inMsgBridge;
return in.messageBuffer(); if (inMsgBridge != null) {
if (currentExecutor == ctx.executor()) {
return ctx.in.messageBuffer();
} else {
BlockingQueue<Object> queue = inMsgBridge.get();
if (queue == null) {
queue = QueueFactory.createQueue();
if (!inMsgBridge.compareAndSet(null, queue)) {
queue = inMsgBridge.get();
}
}
return queue;
}
} }
ctx = ctx.next; ctx = ctx.next;
} }
@ -706,11 +685,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
boolean hasNextOutboundByteBuffer(DefaultChannelHandlerContext ctx) { boolean hasNextOutboundByteBuffer(DefaultChannelHandlerContext ctx) {
for (;;) { for (;;) {
if (ctx == null) { if (ctx == null) {
if (directOutbound.hasByteBuffer()) { return false;
return true;
} else {
return false;
}
} }
ChannelBufferHolder<Object> out = ctx.outbound(); ChannelBufferHolder<Object> out = ctx.outbound();
@ -724,11 +699,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
boolean hasNextOutboundMessageBuffer(DefaultChannelHandlerContext ctx) { boolean hasNextOutboundMessageBuffer(DefaultChannelHandlerContext ctx) {
for (;;) { for (;;) {
if (ctx == null) { if (ctx == null) {
if (directOutbound.hasMessageBuffer()) { return false;
return true;
} else {
return false;
}
} }
ChannelBufferHolder<Object> out = ctx.outbound(); ChannelBufferHolder<Object> out = ctx.outbound();
@ -742,11 +713,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
ChannelBuffer nextOutboundByteBuffer(DefaultChannelHandlerContext ctx) { ChannelBuffer nextOutboundByteBuffer(DefaultChannelHandlerContext ctx) {
for (;;) { for (;;) {
if (ctx == null) { if (ctx == null) {
if (directOutbound.hasByteBuffer()) { throw new NoSuchBufferException();
return directOutbound.byteBuffer();
} else {
throw new NoSuchBufferException();
}
} }
ChannelBufferHolder<Object> out = ctx.outbound(); ChannelBufferHolder<Object> out = ctx.outbound();
@ -757,20 +724,28 @@ public class DefaultChannelPipeline implements ChannelPipeline {
} }
} }
Queue<Object> nextOutboundMessageBuffer(DefaultChannelHandlerContext ctx) { Queue<Object> nextOutboundMessageBuffer(Executor currentExecutor, DefaultChannelHandlerContext ctx) {
for (;;) { for (;;) {
if (ctx == null) { if (ctx == null) {
if (directOutbound.hasMessageBuffer()) { throw new NoSuchBufferException();
return directOutbound.messageBuffer(); }
final AtomicReference<BlockingQueue<Object>> outMsgBridge = ctx.outMsgBridge;
if (outMsgBridge != null) {
if (currentExecutor == ctx.executor()) {
return ctx.out.messageBuffer();
} else { } else {
throw new NoSuchBufferException(); BlockingQueue<Object> queue = outMsgBridge.get();
if (queue == null) {
queue = QueueFactory.createQueue();
if (!outMsgBridge.compareAndSet(null, queue)) {
queue = outMsgBridge.get();
}
}
return queue;
} }
} }
ChannelBufferHolder<Object> out = ctx.outbound();
if (out != null && !out.isBypass() && out.hasMessageBuffer()) {
return out.messageBuffer();
}
ctx = ctx.prev; ctx = ctx.prev;
} }
} }
@ -998,24 +973,20 @@ public class DefaultChannelPipeline implements ChannelPipeline {
} }
validateFuture(future); validateFuture(future);
if (ctx != null) { EventExecutor executor = ctx.executor();
EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) {
if (executor.inEventLoop()) { try {
try { ((ChannelOutboundHandler<Object>) ctx.handler()).bind(ctx, localAddress, future);
((ChannelOutboundHandler<Object>) ctx.handler()).bind(ctx, localAddress, future); } catch (Throwable t) {
} catch (Throwable t) { notifyHandlerException(t);
notifyHandlerException(t);
}
} else {
executor.execute(new Runnable() {
@Override
public void run() {
bind(ctx, localAddress, future);
}
});
} }
} else { } else {
unsafe.bind(localAddress, future); executor.execute(new Runnable() {
@Override
public void run() {
bind(ctx, localAddress, future);
}
});
} }
return future; return future;
} }
@ -1036,24 +1007,20 @@ public class DefaultChannelPipeline implements ChannelPipeline {
} }
validateFuture(future); validateFuture(future);
if (ctx != null) { EventExecutor executor = ctx.executor();
EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) {
if (executor.inEventLoop()) { try {
try { ((ChannelOutboundHandler<Object>) ctx.handler()).connect(ctx, remoteAddress, localAddress, future);
((ChannelOutboundHandler<Object>) ctx.handler()).connect(ctx, remoteAddress, localAddress, future); } catch (Throwable t) {
} catch (Throwable t) { notifyHandlerException(t);
notifyHandlerException(t);
}
} else {
executor.execute(new Runnable() {
@Override
public void run() {
connect(ctx, remoteAddress, localAddress, future);
}
});
} }
} else { } else {
unsafe.connect(remoteAddress, localAddress, future); executor.execute(new Runnable() {
@Override
public void run() {
connect(ctx, remoteAddress, localAddress, future);
}
});
} }
return future; return future;
@ -1066,24 +1033,20 @@ public class DefaultChannelPipeline implements ChannelPipeline {
ChannelFuture disconnect(final DefaultChannelHandlerContext ctx, final ChannelFuture future) { ChannelFuture disconnect(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
validateFuture(future); validateFuture(future);
if (ctx != null) { EventExecutor executor = ctx.executor();
EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) {
if (executor.inEventLoop()) { try {
try { ((ChannelOutboundHandler<Object>) ctx.handler()).disconnect(ctx, future);
((ChannelOutboundHandler<Object>) ctx.handler()).disconnect(ctx, future); } catch (Throwable t) {
} catch (Throwable t) { notifyHandlerException(t);
notifyHandlerException(t);
}
} else {
executor.execute(new Runnable() {
@Override
public void run() {
disconnect(ctx, future);
}
});
} }
} else { } else {
unsafe.disconnect(future); executor.execute(new Runnable() {
@Override
public void run() {
disconnect(ctx, future);
}
});
} }
return future; return future;
@ -1096,24 +1059,20 @@ public class DefaultChannelPipeline implements ChannelPipeline {
ChannelFuture close(final DefaultChannelHandlerContext ctx, final ChannelFuture future) { ChannelFuture close(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
validateFuture(future); validateFuture(future);
if (ctx != null) { EventExecutor executor = ctx.executor();
EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) {
if (executor.inEventLoop()) { try {
try { ((ChannelOutboundHandler<Object>) ctx.handler()).close(ctx, future);
((ChannelOutboundHandler<Object>) ctx.handler()).close(ctx, future); } catch (Throwable t) {
} catch (Throwable t) { notifyHandlerException(t);
notifyHandlerException(t);
}
} else {
executor.execute(new Runnable() {
@Override
public void run() {
close(ctx, future);
}
});
} }
} else { } else {
unsafe.close(future); executor.execute(new Runnable() {
@Override
public void run() {
close(ctx, future);
}
});
} }
return future; return future;
@ -1126,24 +1085,20 @@ public class DefaultChannelPipeline implements ChannelPipeline {
ChannelFuture deregister(final DefaultChannelHandlerContext ctx, final ChannelFuture future) { ChannelFuture deregister(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
validateFuture(future); validateFuture(future);
if (ctx != null) { EventExecutor executor = ctx.executor();
EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) {
if (executor.inEventLoop()) { try {
try { ((ChannelOutboundHandler<Object>) ctx.handler()).deregister(ctx, future);
((ChannelOutboundHandler<Object>) ctx.handler()).deregister(ctx, future); } catch (Throwable t) {
} catch (Throwable t) { notifyHandlerException(t);
notifyHandlerException(t);
}
} else {
executor.execute(new Runnable() {
@Override
public void run() {
deregister(ctx, future);
}
});
} }
} else { } else {
unsafe.deregister(future); executor.execute(new Runnable() {
@Override
public void run() {
deregister(ctx, future);
}
});
} }
return future; return future;
@ -1156,20 +1111,16 @@ public class DefaultChannelPipeline implements ChannelPipeline {
ChannelFuture flush(final DefaultChannelHandlerContext ctx, final ChannelFuture future) { ChannelFuture flush(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
validateFuture(future); validateFuture(future);
if (ctx != null) { EventExecutor executor = ctx.executor();
EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) {
if (executor.inEventLoop()) { flush0(ctx, future);
flush0(ctx, future);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
flush(ctx, future);
}
});
}
} else { } else {
unsafe.flush(future); executor.execute(new Runnable() {
@Override
public void run() {
flush(ctx, future);
}
});
} }
return future; return future;
@ -1177,6 +1128,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private void flush0(final DefaultChannelHandlerContext ctx, ChannelFuture future) { private void flush0(final DefaultChannelHandlerContext ctx, ChannelFuture future) {
try { try {
ctx.flushBridge();
((ChannelOutboundHandler<Object>) ctx.handler()).flush(ctx, future); ((ChannelOutboundHandler<Object>) ctx.handler()).flush(ctx, future);
} catch (Throwable t) { } catch (Throwable t) {
notifyHandlerException(t); notifyHandlerException(t);
@ -1204,16 +1156,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
boolean msgBuf = false; boolean msgBuf = false;
for (;;) { for (;;) {
if (ctx == null) { if (ctx == null) {
executor = channel.eventLoop(); throw new NoSuchBufferException();
out = directOutbound;
if (out.hasByteBuffer()) {
if(!(message instanceof ChannelBuffer)) {
throw new NoSuchBufferException();
}
} else {
msgBuf = true;
}
break;
} }
if (ctx.canHandleOutbound()) { if (ctx.canHandleOutbound()) {
@ -1238,11 +1181,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
ChannelBuffer buf = (ChannelBuffer) message; ChannelBuffer buf = (ChannelBuffer) message;
out.byteBuffer().writeBytes(buf, buf.readerIndex(), buf.readableBytes()); out.byteBuffer().writeBytes(buf, buf.readerIndex(), buf.readableBytes());
} }
if (ctx != null) { flush0(ctx, future);
flush0(ctx, future);
} else {
unsafe.flush(future);
}
return future; return future;
} else { } else {
final DefaultChannelHandlerContext ctx0 = ctx; final DefaultChannelHandlerContext ctx0 = ctx;
@ -1274,7 +1213,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
} }
private DefaultChannelHandlerContext firstInboundContext() { private DefaultChannelHandlerContext firstInboundContext() {
return nextInboundContext(head); return nextInboundContext(head.next);
} }
private DefaultChannelHandlerContext firstOutboundContext() { private DefaultChannelHandlerContext firstOutboundContext() {
@ -1347,16 +1286,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
return inExceptionCaught(cause.getCause()); return inExceptionCaught(cause.getCause());
} }
private void init(EventExecutor executor, String name, ChannelHandler handler) {
DefaultChannelHandlerContext ctx =
new DefaultChannelHandlerContext(this, executor, null, null, name, handler);
callBeforeAdd(ctx);
head = tail = ctx;
name2ctx.clear();
name2ctx.put(name, ctx);
callAfterAdd(ctx);
}
private void checkDuplicateName(String name) { private void checkDuplicateName(String name) {
if (name2ctx.containsKey(name)) { if (name2ctx.containsKey(name)) {
throw new IllegalArgumentException("Duplicate handler name: " + name); throw new IllegalArgumentException("Duplicate handler name: " + name);
@ -1365,7 +1294,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private DefaultChannelHandlerContext getContextOrDie(String name) { private DefaultChannelHandlerContext getContextOrDie(String name) {
DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(name); DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(name);
if (ctx == null) { if (ctx == null || ctx == head) {
throw new NoSuchElementException(name); throw new NoSuchElementException(name);
} else { } else {
return ctx; return ctx;
@ -1374,7 +1303,7 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private DefaultChannelHandlerContext getContextOrDie(ChannelHandler handler) { private DefaultChannelHandlerContext getContextOrDie(ChannelHandler handler) {
DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(handler); DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(handler);
if (ctx == null) { if (ctx == null || ctx == head) {
throw new NoSuchElementException(handler.getClass().getName()); throw new NoSuchElementException(handler.getClass().getName());
} else { } else {
return ctx; return ctx;
@ -1383,10 +1312,84 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private DefaultChannelHandlerContext getContextOrDie(Class<? extends ChannelHandler> handlerType) { private DefaultChannelHandlerContext getContextOrDie(Class<? extends ChannelHandler> handlerType) {
DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(handlerType); DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(handlerType);
if (ctx == null) { if (ctx == null || ctx == head) {
throw new NoSuchElementException(handlerType.getName()); throw new NoSuchElementException(handlerType.getName());
} else { } else {
return ctx; return ctx;
} }
} }
@SuppressWarnings("rawtypes")
private final class HeadHandler implements ChannelOutboundHandler {
@Override
public ChannelBufferHolder newOutboundBuffer(
ChannelOutboundHandlerContext ctx) throws Exception {
switch (channel.type()) {
case STREAM:
return ChannelBufferHolders.byteBuffer();
case MESSAGE:
return ChannelBufferHolders.messageBuffer();
default:
throw new Error();
}
}
@Override
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
// NOOP
}
@Override
public void afterAdd(ChannelHandlerContext ctx) throws Exception {
// NOOP
}
@Override
public void beforeRemove(ChannelHandlerContext ctx) throws Exception {
// NOOP
}
@Override
public void afterRemove(ChannelHandlerContext ctx) throws Exception {
// NOOP
}
@Override
public void bind(ChannelOutboundHandlerContext ctx,
SocketAddress localAddress, ChannelFuture future)
throws Exception {
unsafe.bind(localAddress, future);
}
@Override
public void connect(ChannelOutboundHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelFuture future) throws Exception {
unsafe.connect(remoteAddress, localAddress, future);
}
@Override
public void disconnect(ChannelOutboundHandlerContext ctx,
ChannelFuture future) throws Exception {
unsafe.disconnect(future);
}
@Override
public void close(ChannelOutboundHandlerContext ctx,
ChannelFuture future) throws Exception {
unsafe.close(future);
}
@Override
public void deregister(ChannelOutboundHandlerContext ctx,
ChannelFuture future) throws Exception {
unsafe.deregister(future);
}
@Override
public void flush(ChannelOutboundHandlerContext ctx,
ChannelFuture future) throws Exception {
unsafe.flush(future);
}
}
} }