2008-08-08 00:37:18 +00:00
|
|
|
/*
|
2012-06-04 13:31:44 -07:00
|
|
|
* Copyright 2012 The Netty Project
|
2008-08-08 00:37:18 +00:00
|
|
|
*
|
2011-12-09 14:18:34 +09:00
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
2008-08-08 00:37:18 +00:00
|
|
|
*
|
2012-06-04 13:31:44 -07:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2008-08-08 01:27:24 +00:00
|
|
|
*
|
2009-08-28 07:15:49 +00:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
2011-12-09 14:18:34 +09:00
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
2009-08-28 07:15:49 +00:00
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
2008-08-08 00:37:18 +00:00
|
|
|
*/
|
2011-12-09 12:38:59 +09:00
|
|
|
package io.netty.channel;
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2012-12-17 17:43:45 +09:00
|
|
|
import io.netty.buffer.Buf;
|
2012-06-10 11:08:43 +09:00
|
|
|
import io.netty.buffer.ByteBuf;
|
2012-06-11 10:43:47 +09:00
|
|
|
import io.netty.buffer.MessageBuf;
|
2012-06-11 17:02:00 +09:00
|
|
|
import io.netty.buffer.Unpooled;
|
2012-05-01 17:19:41 +09:00
|
|
|
import io.netty.logging.InternalLogger;
|
|
|
|
import io.netty.logging.InternalLoggerFactory;
|
|
|
|
|
|
|
|
import java.net.SocketAddress;
|
2012-12-18 04:52:46 +09:00
|
|
|
import java.nio.channels.ClosedChannelException;
|
2011-08-02 08:43:10 +09:00
|
|
|
import java.util.ArrayList;
|
2008-08-08 00:37:18 +00:00
|
|
|
import java.util.HashMap;
|
2012-06-01 17:51:19 -07:00
|
|
|
import java.util.IdentityHashMap;
|
2008-08-08 00:37:18 +00:00
|
|
|
import java.util.LinkedHashMap;
|
2011-08-02 08:43:10 +09:00
|
|
|
import java.util.List;
|
2008-08-08 00:37:18 +00:00
|
|
|
import java.util.Map;
|
|
|
|
import java.util.NoSuchElementException;
|
2012-06-05 11:21:44 +02:00
|
|
|
import java.util.concurrent.Future;
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2012-11-10 07:54:33 +09:00
|
|
|
import static io.netty.channel.DefaultChannelHandlerContext.*;
|
|
|
|
|
2008-09-02 07:13:20 +00:00
|
|
|
/**
|
2012-05-01 17:19:41 +09:00
|
|
|
* The default {@link ChannelPipeline} implementation. It is usually created
|
|
|
|
* by a {@link Channel} implementation when the {@link Channel} is created.
|
2008-09-02 07:13:20 +00:00
|
|
|
*/
|
2012-12-23 15:54:14 +01:00
|
|
|
final class DefaultChannelPipeline implements ChannelPipeline {
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2008-08-09 15:05:53 +00:00
|
|
|
static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class);
|
2009-03-11 10:45:55 +00:00
|
|
|
|
2012-06-01 17:51:19 -07:00
|
|
|
final Channel channel;
|
2012-06-01 18:34:19 -07:00
|
|
|
private final Channel.Unsafe unsafe;
|
|
|
|
|
2012-06-07 14:52:33 +09:00
|
|
|
final DefaultChannelHandlerContext head;
|
2008-08-08 00:37:18 +00:00
|
|
|
private volatile DefaultChannelHandlerContext tail;
|
|
|
|
private final Map<String, DefaultChannelHandlerContext> name2ctx =
|
|
|
|
new HashMap<String, DefaultChannelHandlerContext>(4);
|
2012-05-30 11:32:39 -07:00
|
|
|
private boolean firedChannelActive;
|
2012-06-04 11:56:00 -07:00
|
|
|
private boolean fireInboundBufferUpdatedOnActivation;
|
|
|
|
|
2012-08-10 20:17:18 +09:00
|
|
|
final Map<EventExecutorGroup, EventExecutor> childExecutors =
|
|
|
|
new IdentityHashMap<EventExecutorGroup, EventExecutor>();
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
public DefaultChannelPipeline(Channel channel) {
|
2008-08-08 00:37:18 +00:00
|
|
|
if (channel == null) {
|
|
|
|
throw new NullPointerException("channel");
|
|
|
|
}
|
|
|
|
this.channel = channel;
|
2012-06-03 18:51:42 -07:00
|
|
|
|
|
|
|
HeadHandler headHandler = new HeadHandler();
|
|
|
|
head = new DefaultChannelHandlerContext(
|
|
|
|
this, null, null, null, generateName(headHandler), headHandler);
|
|
|
|
tail = head;
|
|
|
|
|
2012-06-01 18:34:19 -07:00
|
|
|
unsafe = channel.unsafe();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-05-01 17:19:41 +09:00
|
|
|
public Channel channel() {
|
|
|
|
return channel;
|
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-01 17:51:19 -07:00
|
|
|
public ChannelPipeline addFirst(String name, ChannelHandler handler) {
|
|
|
|
return addFirst(null, name, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-11-12 14:55:05 -08:00
|
|
|
public ChannelPipeline addFirst(EventExecutorGroup group, final String name, ChannelHandler handler) {
|
|
|
|
final DefaultChannelHandlerContext nextCtx;
|
|
|
|
final DefaultChannelHandlerContext newCtx;
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
checkDuplicateName(name);
|
|
|
|
nextCtx = head.next;
|
|
|
|
newCtx = new DefaultChannelHandlerContext(this, group, head, nextCtx, name, handler);
|
|
|
|
|
|
|
|
if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
|
|
|
|
addFirst0(name, nextCtx, newCtx);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
// Run the following 'waiting' code outside of the above synchronized block
|
|
|
|
// in order to avoid deadlock
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
newCtx.executeOnEventLoop(new Runnable() {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
checkDuplicateName(name);
|
|
|
|
addFirst0(name, nextCtx, newCtx);
|
2012-11-12 14:55:05 -08:00
|
|
|
}
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
}
|
|
|
|
});
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
return this;
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-06-08 19:28:12 +09:00
|
|
|
private void addFirst0(
|
|
|
|
final String name, DefaultChannelHandlerContext nextCtx, DefaultChannelHandlerContext newCtx) {
|
2012-06-03 18:51:42 -07:00
|
|
|
callBeforeAdd(newCtx);
|
2008-12-01 10:07:54 +00:00
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
if (nextCtx != null) {
|
|
|
|
nextCtx.prev = newCtx;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-06-03 18:51:42 -07:00
|
|
|
head.next = newCtx;
|
2012-11-16 13:30:34 +08:00
|
|
|
if (tail == head) {
|
|
|
|
tail = newCtx;
|
|
|
|
}
|
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
name2ctx.put(name, newCtx);
|
2012-05-15 14:08:42 +09:00
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
callAfterAdd(newCtx);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-01 17:51:19 -07:00
|
|
|
public ChannelPipeline addLast(String name, ChannelHandler handler) {
|
|
|
|
return addLast(null, name, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-11-12 14:55:05 -08:00
|
|
|
public ChannelPipeline addLast(EventExecutorGroup group, final String name, ChannelHandler handler) {
|
|
|
|
final DefaultChannelHandlerContext oldTail;
|
|
|
|
final DefaultChannelHandlerContext newTail;
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
synchronized (this) {
|
|
|
|
checkDuplicateName(name);
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
oldTail = tail;
|
|
|
|
newTail = new DefaultChannelHandlerContext(this, group, oldTail, null, name, handler);
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
if (!newTail.channel().isRegistered() || newTail.executor().inEventLoop()) {
|
|
|
|
addLast0(name, oldTail, newTail);
|
|
|
|
return this;
|
2012-06-04 20:49:31 +02:00
|
|
|
}
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
// Run the following 'waiting' code outside of the above synchronized block
|
|
|
|
// in order to avoid deadlock
|
|
|
|
|
|
|
|
newTail.executeOnEventLoop(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
checkDuplicateName(name);
|
|
|
|
addLast0(name, oldTail, newTail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return this;
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2012-06-08 19:28:12 +09:00
|
|
|
private void addLast0(
|
|
|
|
final String name, DefaultChannelHandlerContext oldTail, DefaultChannelHandlerContext newTail) {
|
2012-06-03 18:51:42 -07:00
|
|
|
callBeforeAdd(newTail);
|
2008-12-01 10:07:54 +00:00
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
oldTail.next = newTail;
|
|
|
|
tail = newTail;
|
|
|
|
name2ctx.put(name, newTail);
|
2012-05-15 14:08:42 +09:00
|
|
|
|
2012-06-04 11:56:00 -07:00
|
|
|
callAfterAdd(newTail);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-01 17:51:19 -07:00
|
|
|
public ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler) {
|
|
|
|
return addBefore(null, baseName, name, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-08 19:28:12 +09:00
|
|
|
public ChannelPipeline addBefore(
|
2012-11-12 14:55:05 -08:00
|
|
|
EventExecutorGroup group, String baseName, final String name, ChannelHandler handler) {
|
|
|
|
final DefaultChannelHandlerContext ctx;
|
|
|
|
final DefaultChannelHandlerContext newCtx;
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
ctx = getContextOrDie(baseName);
|
|
|
|
checkDuplicateName(name);
|
|
|
|
newCtx = new DefaultChannelHandlerContext(this, group, ctx.prev, ctx, name, handler);
|
|
|
|
|
|
|
|
if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
|
|
|
|
addBefore0(name, ctx, newCtx);
|
|
|
|
return this;
|
2012-06-04 20:49:31 +02:00
|
|
|
}
|
2012-11-12 14:55:05 -08:00
|
|
|
}
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
// Run the following 'waiting' code outside of the above synchronized block
|
|
|
|
// in order to avoid deadlock
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
newCtx.executeOnEventLoop(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
checkDuplicateName(name);
|
|
|
|
addBefore0(name, ctx, newCtx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
return this;
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
|
|
|
|
2012-06-04 11:56:00 -07:00
|
|
|
private void addBefore0(final String name, DefaultChannelHandlerContext ctx, DefaultChannelHandlerContext newCtx) {
|
2012-06-03 18:51:42 -07:00
|
|
|
callBeforeAdd(newCtx);
|
2008-12-01 10:07:54 +00:00
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
ctx.prev.next = newCtx;
|
|
|
|
ctx.prev = newCtx;
|
|
|
|
name2ctx.put(name, newCtx);
|
2012-05-15 14:08:42 +09:00
|
|
|
|
2012-06-04 11:56:00 -07:00
|
|
|
callAfterAdd(newCtx);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-01 17:51:19 -07:00
|
|
|
public ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler) {
|
|
|
|
return addAfter(null, baseName, name, handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-08 19:28:12 +09:00
|
|
|
public ChannelPipeline addAfter(
|
2012-11-12 14:55:05 -08:00
|
|
|
EventExecutorGroup group, String baseName, final String name, ChannelHandler handler) {
|
|
|
|
final DefaultChannelHandlerContext ctx;
|
|
|
|
final DefaultChannelHandlerContext newCtx;
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
ctx = getContextOrDie(baseName);
|
|
|
|
if (ctx == tail) {
|
|
|
|
return addLast(name, handler);
|
|
|
|
}
|
|
|
|
checkDuplicateName(name);
|
|
|
|
newCtx = new DefaultChannelHandlerContext(this, group, ctx, ctx.next, name, handler);
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
|
|
|
|
addAfter0(name, ctx, newCtx);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
// Run the following 'waiting' code outside of the above synchronized block
|
|
|
|
// in order to avoid deadlock
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
newCtx.executeOnEventLoop(new Runnable() {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
checkDuplicateName(name);
|
|
|
|
addAfter0(name, ctx, newCtx);
|
2012-06-04 20:49:31 +02:00
|
|
|
}
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
}
|
|
|
|
});
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
return this;
|
2012-05-15 14:08:42 +09:00
|
|
|
}
|
|
|
|
|
2012-06-04 11:56:00 -07:00
|
|
|
private void addAfter0(final String name, DefaultChannelHandlerContext ctx, DefaultChannelHandlerContext newCtx) {
|
2012-06-04 20:34:09 +02:00
|
|
|
checkDuplicateName(name);
|
|
|
|
|
|
|
|
callBeforeAdd(newCtx);
|
|
|
|
|
|
|
|
ctx.next.prev = newCtx;
|
|
|
|
ctx.next = newCtx;
|
|
|
|
name2ctx.put(name, newCtx);
|
|
|
|
|
2012-06-04 11:56:00 -07:00
|
|
|
callAfterAdd(newCtx);
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2012-05-15 14:08:42 +09:00
|
|
|
@Override
|
|
|
|
public ChannelPipeline addFirst(ChannelHandler... handlers) {
|
2012-06-01 17:51:19 -07:00
|
|
|
return addFirst(null, handlers);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-08-10 20:17:18 +09:00
|
|
|
public ChannelPipeline addFirst(EventExecutorGroup executor, ChannelHandler... handlers) {
|
2012-05-15 14:08:42 +09:00
|
|
|
if (handlers == null) {
|
|
|
|
throw new NullPointerException("handlers");
|
|
|
|
}
|
2012-05-15 14:49:23 +09:00
|
|
|
if (handlers.length == 0 || handlers[0] == null) {
|
2012-05-15 14:08:42 +09:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
int size;
|
|
|
|
for (size = 1; size < handlers.length; size ++) {
|
|
|
|
if (handlers[size] == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = size - 1; i >= 0; i --) {
|
|
|
|
ChannelHandler h = handlers[i];
|
2012-06-01 17:51:19 -07:00
|
|
|
addFirst(executor, generateName(h), h);
|
2012-05-15 14:08:42 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelPipeline addLast(ChannelHandler... handlers) {
|
2012-06-01 17:51:19 -07:00
|
|
|
return addLast(null, handlers);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-08-10 20:17:18 +09:00
|
|
|
public ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
|
2012-05-15 14:08:42 +09:00
|
|
|
if (handlers == null) {
|
|
|
|
throw new NullPointerException("handlers");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ChannelHandler h: handlers) {
|
|
|
|
if (h == null) {
|
|
|
|
break;
|
|
|
|
}
|
2012-06-01 17:51:19 -07:00
|
|
|
addLast(executor, generateName(h), h);
|
2012-05-15 14:08:42 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2012-05-29 16:41:26 -07:00
|
|
|
private static String generateName(ChannelHandler handler) {
|
2012-05-15 14:08:42 +09:00
|
|
|
String type = handler.getClass().getSimpleName();
|
|
|
|
StringBuilder buf = new StringBuilder(type.length() + 10);
|
|
|
|
buf.append(type);
|
|
|
|
buf.append("-0");
|
|
|
|
buf.append(Long.toHexString(System.identityHashCode(handler) & 0xFFFFFFFFL | 0x100000000L));
|
|
|
|
buf.setCharAt(buf.length() - 9, 'x');
|
|
|
|
return buf.toString();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-12-14 17:06:31 +01:00
|
|
|
public ChannelPipeline remove(ChannelHandler handler) {
|
2008-08-08 00:37:18 +00:00
|
|
|
remove(getContextOrDie(handler));
|
2012-12-14 17:06:31 +01:00
|
|
|
return this;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-05 11:21:44 +02:00
|
|
|
public ChannelHandler remove(String name) {
|
2012-05-01 17:19:41 +09:00
|
|
|
return remove(getContextOrDie(name)).handler();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2012-09-21 22:33:11 +02:00
|
|
|
@SuppressWarnings("unchecked")
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-05 11:21:44 +02:00
|
|
|
public <T extends ChannelHandler> T remove(Class<T> handlerType) {
|
2012-05-01 17:19:41 +09:00
|
|
|
return (T) remove(getContextOrDie(handlerType)).handler();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2012-06-04 20:34:09 +02:00
|
|
|
private DefaultChannelHandlerContext remove(final DefaultChannelHandlerContext ctx) {
|
2012-11-12 14:55:05 -08:00
|
|
|
DefaultChannelHandlerContext context;
|
2012-11-16 06:04:37 +09:00
|
|
|
Future<?> future;
|
2012-11-12 14:55:05 -08:00
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
if (head == tail) {
|
|
|
|
return null;
|
|
|
|
} else if (ctx == head) {
|
|
|
|
throw new Error(); // Should never happen.
|
|
|
|
} else if (ctx == tail) {
|
2012-06-05 11:21:44 +02:00
|
|
|
if (head == tail) {
|
2012-11-12 14:55:05 -08:00
|
|
|
throw new NoSuchElementException();
|
|
|
|
}
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
final DefaultChannelHandlerContext oldTail = tail;
|
|
|
|
if (!oldTail.channel().isRegistered() || oldTail.executor().inEventLoop()) {
|
|
|
|
removeLast0(oldTail);
|
|
|
|
return oldTail;
|
|
|
|
} else {
|
|
|
|
future = oldTail.executor().submit(new Runnable() {
|
2012-06-05 11:21:44 +02:00
|
|
|
@Override
|
2012-11-12 14:55:05 -08:00
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
removeLast0(oldTail);
|
|
|
|
}
|
2012-06-05 11:21:44 +02:00
|
|
|
}
|
|
|
|
});
|
2012-11-12 14:55:05 -08:00
|
|
|
context = oldTail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!ctx.channel().isRegistered() || ctx.executor().inEventLoop()) {
|
|
|
|
remove0(ctx);
|
|
|
|
return ctx;
|
2012-06-05 11:21:44 +02:00
|
|
|
} else {
|
2012-11-12 14:55:05 -08:00
|
|
|
future = ctx.executor().submit(new Runnable() {
|
2012-06-08 10:57:38 +09:00
|
|
|
@Override
|
2012-11-12 14:55:05 -08:00
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
remove0(ctx);
|
|
|
|
}
|
2012-06-08 10:57:38 +09:00
|
|
|
}
|
|
|
|
});
|
2012-11-12 14:55:05 -08:00
|
|
|
context = ctx;
|
2012-06-04 20:49:31 +02:00
|
|
|
}
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2012-11-12 14:55:05 -08:00
|
|
|
}
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
// Run the following 'waiting' code outside of the above synchronized block
|
|
|
|
// in order to avoid deadlock
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-16 06:04:37 +09:00
|
|
|
waitForFuture(future);
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
return context;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2012-06-04 20:34:09 +02:00
|
|
|
private void remove0(DefaultChannelHandlerContext ctx) {
|
|
|
|
callBeforeRemove(ctx);
|
|
|
|
|
|
|
|
DefaultChannelHandlerContext prev = ctx.prev;
|
|
|
|
DefaultChannelHandlerContext next = ctx.next;
|
|
|
|
prev.next = next;
|
|
|
|
next.prev = prev;
|
|
|
|
name2ctx.remove(ctx.name());
|
|
|
|
|
|
|
|
callAfterRemove(ctx);
|
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-05 11:21:44 +02:00
|
|
|
public ChannelHandler removeFirst() {
|
2012-06-03 18:51:42 -07:00
|
|
|
if (head == tail) {
|
2008-08-18 02:38:54 +00:00
|
|
|
throw new NoSuchElementException();
|
|
|
|
}
|
2012-06-03 18:51:42 -07:00
|
|
|
return remove(head.next).handler();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-05 11:21:44 +02:00
|
|
|
public ChannelHandler removeLast() {
|
2012-11-12 14:55:05 -08:00
|
|
|
final DefaultChannelHandlerContext oldTail;
|
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
if (head == tail) {
|
|
|
|
throw new NoSuchElementException();
|
2012-06-04 20:49:31 +02:00
|
|
|
}
|
2012-11-12 14:55:05 -08:00
|
|
|
oldTail = tail;
|
|
|
|
if (!oldTail.channel().isRegistered() || oldTail.executor().inEventLoop()) {
|
|
|
|
removeLast0(oldTail);
|
|
|
|
return oldTail.handler();
|
2012-06-05 11:34:47 +02:00
|
|
|
}
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
// Run the following 'waiting' code outside of the above synchronized block
|
|
|
|
// in order to avoid deadlock
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
oldTail.executeOnEventLoop(new Runnable() {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
removeLast0(oldTail);
|
2012-11-12 14:55:05 -08:00
|
|
|
}
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
}
|
|
|
|
});
|
2012-11-12 14:55:05 -08:00
|
|
|
|
|
|
|
return oldTail.handler();
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2012-06-04 20:34:09 +02:00
|
|
|
private void removeLast0(DefaultChannelHandlerContext oldTail) {
|
2008-12-01 10:07:54 +00:00
|
|
|
callBeforeRemove(oldTail);
|
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
oldTail.prev.next = null;
|
|
|
|
tail = oldTail.prev;
|
|
|
|
name2ctx.remove(oldTail.name());
|
2008-12-01 10:07:54 +00:00
|
|
|
|
|
|
|
callBeforeRemove(oldTail);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-12-14 17:06:31 +01:00
|
|
|
public ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {
|
2008-08-08 00:37:18 +00:00
|
|
|
replace(getContextOrDie(oldHandler), newName, newHandler);
|
2012-12-14 17:06:31 +01:00
|
|
|
return this;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-06-05 11:21:44 +02:00
|
|
|
public ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) {
|
2008-08-08 00:37:18 +00:00
|
|
|
return replace(getContextOrDie(oldName), newName, newHandler);
|
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2008-08-08 00:37:18 +00:00
|
|
|
@SuppressWarnings("unchecked")
|
2012-06-05 11:21:44 +02:00
|
|
|
public <T extends ChannelHandler> T replace(
|
2008-08-08 00:37:18 +00:00
|
|
|
Class<T> oldHandlerType, String newName, ChannelHandler newHandler) {
|
|
|
|
return (T) replace(getContextOrDie(oldHandlerType), newName, newHandler);
|
|
|
|
}
|
|
|
|
|
2012-06-08 19:28:12 +09:00
|
|
|
private ChannelHandler replace(
|
2012-11-12 14:55:05 -08:00
|
|
|
final DefaultChannelHandlerContext ctx, final String newName, ChannelHandler newHandler) {
|
2012-11-16 06:04:37 +09:00
|
|
|
Future<?> future;
|
2012-11-12 14:55:05 -08:00
|
|
|
synchronized (this) {
|
|
|
|
if (ctx == head) {
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
} else if (ctx == tail) {
|
|
|
|
if (head == tail) {
|
|
|
|
throw new NoSuchElementException();
|
|
|
|
}
|
|
|
|
final DefaultChannelHandlerContext oldTail = tail;
|
|
|
|
final DefaultChannelHandlerContext newTail =
|
|
|
|
new DefaultChannelHandlerContext(this, null, oldTail, null, newName, newHandler);
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
if (!oldTail.channel().isRegistered() || oldTail.executor().inEventLoop()) {
|
|
|
|
removeLast0(oldTail);
|
|
|
|
checkDuplicateName(newName);
|
|
|
|
addLast0(newName, tail, newTail);
|
|
|
|
return ctx.handler();
|
|
|
|
} else {
|
|
|
|
future = oldTail.executor().submit(new Runnable() {
|
2012-06-08 10:57:38 +09:00
|
|
|
@Override
|
2012-11-12 14:55:05 -08:00
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
removeLast0(oldTail);
|
|
|
|
checkDuplicateName(newName);
|
|
|
|
addLast0(newName, tail, newTail);
|
|
|
|
}
|
2012-06-08 10:57:38 +09:00
|
|
|
}
|
|
|
|
});
|
2012-11-12 14:55:05 -08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
boolean sameName = ctx.name().equals(newName);
|
|
|
|
if (!sameName) {
|
|
|
|
checkDuplicateName(newName);
|
|
|
|
}
|
2008-12-01 10:07:54 +00:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
DefaultChannelHandlerContext prev = ctx.prev;
|
|
|
|
DefaultChannelHandlerContext next = ctx.next;
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
final DefaultChannelHandlerContext newCtx =
|
|
|
|
new DefaultChannelHandlerContext(this, ctx.executor, prev, next, newName, newHandler);
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
|
|
|
|
replace0(ctx, newName, newCtx);
|
|
|
|
return ctx.handler();
|
|
|
|
} else {
|
|
|
|
future = newCtx.executor().submit(new Runnable() {
|
2012-06-05 11:21:44 +02:00
|
|
|
@Override
|
2012-11-12 14:55:05 -08:00
|
|
|
public void run() {
|
|
|
|
synchronized (DefaultChannelPipeline.this) {
|
|
|
|
replace0(ctx, newName, newCtx);
|
|
|
|
}
|
2012-06-05 11:21:44 +02:00
|
|
|
}
|
|
|
|
});
|
2012-06-04 20:49:31 +02:00
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-11-12 14:55:05 -08:00
|
|
|
}
|
2012-06-06 23:02:47 +09:00
|
|
|
|
2012-11-12 14:55:05 -08:00
|
|
|
// Run the following 'waiting' code outside of the above synchronized block
|
|
|
|
// in order to avoid deadlock
|
2012-06-05 11:21:44 +02:00
|
|
|
|
2012-11-16 06:04:37 +09:00
|
|
|
waitForFuture(future);
|
2012-11-12 14:55:05 -08:00
|
|
|
|
|
|
|
return ctx.handler();
|
2012-06-04 20:34:09 +02:00
|
|
|
}
|
2008-12-01 10:07:54 +00:00
|
|
|
|
2012-06-04 11:56:00 -07:00
|
|
|
private void replace0(DefaultChannelHandlerContext ctx, String newName, DefaultChannelHandlerContext newCtx) {
|
2012-06-04 20:34:09 +02:00
|
|
|
boolean sameName = ctx.name().equals(newName);
|
2008-12-01 10:07:54 +00:00
|
|
|
|
2012-06-04 20:34:09 +02:00
|
|
|
DefaultChannelHandlerContext prev = ctx.prev;
|
|
|
|
DefaultChannelHandlerContext next = ctx.next;
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2012-06-04 20:34:09 +02:00
|
|
|
callBeforeRemove(ctx);
|
|
|
|
callBeforeAdd(newCtx);
|
|
|
|
|
|
|
|
prev.next = newCtx;
|
|
|
|
next.prev = newCtx;
|
|
|
|
|
|
|
|
if (!sameName) {
|
|
|
|
name2ctx.remove(ctx.name());
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-06-04 20:34:09 +02:00
|
|
|
name2ctx.put(newName, newCtx);
|
2008-12-01 10:07:54 +00:00
|
|
|
|
2012-12-21 17:10:36 +01:00
|
|
|
ChannelPipelineException removeException = null;
|
|
|
|
ChannelPipelineException addException = null;
|
2012-06-04 20:34:09 +02:00
|
|
|
boolean removed = false;
|
|
|
|
try {
|
|
|
|
callAfterRemove(ctx);
|
|
|
|
removed = true;
|
2012-12-21 17:10:36 +01:00
|
|
|
} catch (ChannelPipelineException e) {
|
2012-06-04 20:34:09 +02:00
|
|
|
removeException = e;
|
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2012-06-04 20:34:09 +02:00
|
|
|
boolean added = false;
|
|
|
|
try {
|
|
|
|
callAfterAdd(newCtx);
|
|
|
|
added = true;
|
2012-12-21 17:10:36 +01:00
|
|
|
} catch (ChannelPipelineException e) {
|
2012-06-04 20:34:09 +02:00
|
|
|
addException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!removed && !added) {
|
|
|
|
logger.warn(removeException.getMessage(), removeException);
|
|
|
|
logger.warn(addException.getMessage(), addException);
|
2012-12-21 17:10:36 +01:00
|
|
|
throw new ChannelPipelineException(
|
2012-06-04 20:34:09 +02:00
|
|
|
"Both " + ctx.handler().getClass().getName() +
|
|
|
|
".afterRemove() and " + newCtx.handler().getClass().getName() +
|
|
|
|
".afterAdd() failed; see logs.");
|
|
|
|
} else if (!removed) {
|
|
|
|
throw removeException;
|
|
|
|
} else if (!added) {
|
|
|
|
throw addException;
|
|
|
|
}
|
|
|
|
}
|
2012-06-04 11:56:00 -07:00
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
private static void callBeforeAdd(ChannelHandlerContext ctx) {
|
2012-05-31 14:54:48 -07:00
|
|
|
ChannelHandler handler = ctx.handler();
|
2012-06-07 14:52:33 +09:00
|
|
|
if (handler instanceof ChannelStateHandlerAdapter) {
|
|
|
|
ChannelStateHandlerAdapter h = (ChannelStateHandlerAdapter) handler;
|
2012-05-31 14:54:48 -07:00
|
|
|
if (!h.isSharable() && h.added) {
|
2012-12-21 17:10:36 +01:00
|
|
|
throw new ChannelPipelineException(
|
2012-11-18 23:11:39 +13:00
|
|
|
h.getClass().getName() +
|
|
|
|
" is not a @Sharable handler, so can't be added or removed multiple times.");
|
2012-05-31 14:54:48 -07:00
|
|
|
}
|
|
|
|
h.added = true;
|
|
|
|
}
|
2008-12-01 10:07:54 +00:00
|
|
|
try {
|
2012-05-31 14:54:48 -07:00
|
|
|
handler.beforeAdd(ctx);
|
2008-12-01 10:07:54 +00:00
|
|
|
} catch (Throwable t) {
|
2012-12-21 17:10:36 +01:00
|
|
|
throw new ChannelPipelineException(
|
2012-05-31 14:54:48 -07:00
|
|
|
handler.getClass().getName() +
|
2008-12-01 10:07:54 +00:00
|
|
|
".beforeAdd() has thrown an exception; not adding.", t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void callAfterAdd(ChannelHandlerContext ctx) {
|
|
|
|
try {
|
2012-05-01 17:19:41 +09:00
|
|
|
ctx.handler().afterAdd(ctx);
|
2008-12-01 10:07:54 +00:00
|
|
|
} catch (Throwable t) {
|
|
|
|
boolean removed = false;
|
|
|
|
try {
|
|
|
|
remove((DefaultChannelHandlerContext) ctx);
|
|
|
|
removed = true;
|
|
|
|
} catch (Throwable t2) {
|
2012-02-17 10:37:41 +01:00
|
|
|
if (logger.isWarnEnabled()) {
|
2012-05-01 17:19:41 +09:00
|
|
|
logger.warn("Failed to remove a handler: " + ctx.name(), t2);
|
2012-02-17 10:37:41 +01:00
|
|
|
}
|
2008-12-01 10:07:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (removed) {
|
2012-12-21 17:10:36 +01:00
|
|
|
throw new ChannelPipelineException(
|
2012-05-01 17:19:41 +09:00
|
|
|
ctx.handler().getClass().getName() +
|
2008-12-01 10:07:54 +00:00
|
|
|
".afterAdd() has thrown an exception; removed.", t);
|
|
|
|
} else {
|
2012-12-21 17:10:36 +01:00
|
|
|
throw new ChannelPipelineException(
|
2012-05-01 17:19:41 +09:00
|
|
|
ctx.handler().getClass().getName() +
|
2008-12-01 10:07:54 +00:00
|
|
|
".afterAdd() has thrown an exception; also failed to remove.", t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
private static void callBeforeRemove(ChannelHandlerContext ctx) {
|
2008-12-01 10:07:54 +00:00
|
|
|
try {
|
2012-05-01 17:19:41 +09:00
|
|
|
ctx.handler().beforeRemove(ctx);
|
2008-12-01 10:07:54 +00:00
|
|
|
} catch (Throwable t) {
|
2012-12-21 17:10:36 +01:00
|
|
|
throw new ChannelPipelineException(
|
2012-05-01 17:19:41 +09:00
|
|
|
ctx.handler().getClass().getName() +
|
2008-12-01 10:07:54 +00:00
|
|
|
".beforeRemove() has thrown an exception; not removing.", t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
private static void callAfterRemove(ChannelHandlerContext ctx) {
|
2008-12-01 10:07:54 +00:00
|
|
|
try {
|
2012-05-01 17:19:41 +09:00
|
|
|
ctx.handler().afterRemove(ctx);
|
2008-12-01 10:07:54 +00:00
|
|
|
} catch (Throwable t) {
|
2012-12-21 17:10:36 +01:00
|
|
|
throw new ChannelPipelineException(
|
2012-05-01 17:19:41 +09:00
|
|
|
ctx.handler().getClass().getName() +
|
2008-12-01 10:07:54 +00:00
|
|
|
".afterRemove() has thrown an exception.", t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-08-28 13:03:41 +09:00
|
|
|
public ChannelHandler first() {
|
2012-06-03 18:51:42 -07:00
|
|
|
DefaultChannelHandlerContext first = head.next;
|
|
|
|
if (first == null) {
|
2008-08-18 02:27:11 +00:00
|
|
|
return null;
|
|
|
|
}
|
2012-06-03 18:51:42 -07:00
|
|
|
return first.handler();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-08-28 13:03:41 +09:00
|
|
|
public ChannelHandlerContext firstContext() {
|
|
|
|
return head.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelHandler last() {
|
2012-06-03 18:51:42 -07:00
|
|
|
DefaultChannelHandlerContext last = tail;
|
|
|
|
if (last == head || last == null) {
|
2008-08-18 02:27:11 +00:00
|
|
|
return null;
|
|
|
|
}
|
2012-06-03 18:51:42 -07:00
|
|
|
return last.handler();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-08-28 13:03:41 +09:00
|
|
|
public ChannelHandlerContext lastContext() {
|
|
|
|
DefaultChannelHandlerContext last = tail;
|
|
|
|
if (last == head || last == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return last;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelHandler get(String name) {
|
|
|
|
ChannelHandlerContext ctx = context(name);
|
2008-08-08 00:37:18 +00:00
|
|
|
if (ctx == null) {
|
|
|
|
return null;
|
|
|
|
} else {
|
2012-05-01 17:19:41 +09:00
|
|
|
return ctx.handler();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-21 22:33:11 +02:00
|
|
|
@SuppressWarnings("unchecked")
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-08-28 13:03:41 +09:00
|
|
|
public <T extends ChannelHandler> T get(Class<T> handlerType) {
|
2012-05-01 17:19:41 +09:00
|
|
|
ChannelHandlerContext ctx = context(handlerType);
|
2008-08-08 00:37:18 +00:00
|
|
|
if (ctx == null) {
|
|
|
|
return null;
|
|
|
|
} else {
|
2012-05-01 17:19:41 +09:00
|
|
|
return (T) ctx.handler();
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-08-28 13:03:41 +09:00
|
|
|
public ChannelHandlerContext context(String name) {
|
2008-08-08 00:37:18 +00:00
|
|
|
if (name == null) {
|
|
|
|
throw new NullPointerException("name");
|
|
|
|
}
|
2012-08-28 13:03:41 +09:00
|
|
|
|
|
|
|
synchronized (this) {
|
|
|
|
return name2ctx.get(name);
|
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-08-28 13:03:41 +09:00
|
|
|
public ChannelHandlerContext context(ChannelHandler handler) {
|
2008-08-08 00:37:18 +00:00
|
|
|
if (handler == null) {
|
|
|
|
throw new NullPointerException("handler");
|
|
|
|
}
|
2012-08-28 13:03:41 +09:00
|
|
|
|
|
|
|
DefaultChannelHandlerContext ctx = head.next;
|
2008-08-08 00:37:18 +00:00
|
|
|
for (;;) {
|
2012-08-28 13:03:41 +09:00
|
|
|
if (ctx == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
if (ctx.handler() == handler) {
|
2008-08-08 00:37:18 +00:00
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx = ctx.next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-08-28 13:03:41 +09:00
|
|
|
public ChannelHandlerContext context(Class<? extends ChannelHandler> handlerType) {
|
2009-12-17 10:57:57 +00:00
|
|
|
if (handlerType == null) {
|
|
|
|
throw new NullPointerException("handlerType");
|
|
|
|
}
|
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
DefaultChannelHandlerContext ctx = head.next;
|
2008-08-08 00:37:18 +00:00
|
|
|
for (;;) {
|
2012-08-28 13:03:41 +09:00
|
|
|
if (ctx == null) {
|
|
|
|
return null;
|
|
|
|
}
|
2012-05-01 17:19:41 +09:00
|
|
|
if (handlerType.isAssignableFrom(ctx.handler().getClass())) {
|
2008-08-08 00:37:18 +00:00
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
ctx = ctx.next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-02 08:43:10 +09:00
|
|
|
@Override
|
2012-05-01 17:19:41 +09:00
|
|
|
public List<String> names() {
|
2011-08-02 08:43:10 +09:00
|
|
|
List<String> list = new ArrayList<String>();
|
2012-06-03 18:51:42 -07:00
|
|
|
DefaultChannelHandlerContext ctx = head.next;
|
2011-08-02 08:43:10 +09:00
|
|
|
for (;;) {
|
|
|
|
if (ctx == null) {
|
2012-08-28 13:03:41 +09:00
|
|
|
return list;
|
2011-08-02 08:43:10 +09:00
|
|
|
}
|
2012-08-28 13:03:41 +09:00
|
|
|
list.add(ctx.name());
|
|
|
|
ctx = ctx.next;
|
2011-08-02 08:43:10 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2008-08-08 00:37:18 +00:00
|
|
|
public Map<String, ChannelHandler> toMap() {
|
|
|
|
Map<String, ChannelHandler> map = new LinkedHashMap<String, ChannelHandler>();
|
2012-06-03 18:51:42 -07:00
|
|
|
DefaultChannelHandlerContext ctx = head.next;
|
2008-08-08 00:37:18 +00:00
|
|
|
for (;;) {
|
|
|
|
if (ctx == null) {
|
2012-08-28 13:03:41 +09:00
|
|
|
return map;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-08-28 13:03:41 +09:00
|
|
|
map.put(ctx.name(), ctx.handler());
|
|
|
|
ctx = ctx.next;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-03 04:09:46 +00:00
|
|
|
/**
|
|
|
|
* Returns the {@link String} representation of this pipeline.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
StringBuilder buf = new StringBuilder();
|
|
|
|
buf.append(getClass().getSimpleName());
|
|
|
|
buf.append('{');
|
2012-06-03 18:51:42 -07:00
|
|
|
DefaultChannelHandlerContext ctx = head.next;
|
2008-09-03 04:09:46 +00:00
|
|
|
for (;;) {
|
2012-08-28 13:03:41 +09:00
|
|
|
if (ctx == null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-09-03 04:09:46 +00:00
|
|
|
buf.append('(');
|
2012-05-01 17:19:41 +09:00
|
|
|
buf.append(ctx.name());
|
2008-09-03 04:09:46 +00:00
|
|
|
buf.append(" = ");
|
2012-05-01 17:19:41 +09:00
|
|
|
buf.append(ctx.handler().getClass().getName());
|
2008-09-03 04:09:46 +00:00
|
|
|
buf.append(')');
|
2012-08-28 13:03:41 +09:00
|
|
|
|
2008-09-03 04:09:46 +00:00
|
|
|
ctx = ctx.next;
|
|
|
|
if (ctx == null) {
|
|
|
|
break;
|
|
|
|
}
|
2012-08-28 13:03:41 +09:00
|
|
|
|
2008-09-03 04:09:46 +00:00
|
|
|
buf.append(", ");
|
|
|
|
}
|
|
|
|
buf.append('}');
|
|
|
|
return buf.toString();
|
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-11-30 22:49:51 +09:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public <T> MessageBuf<T> inboundMessageBuffer() {
|
|
|
|
return (MessageBuf<T>) head.nextInboundMessageBuffer();
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-10 11:08:43 +09:00
|
|
|
public ByteBuf inboundByteBuffer() {
|
2012-06-08 23:11:15 +09:00
|
|
|
return head.nextInboundByteBuffer();
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-11-30 22:49:51 +09:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public <T> MessageBuf<T> outboundMessageBuffer() {
|
|
|
|
return (MessageBuf<T>) nextOutboundMessageBuffer(tail);
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-10 11:08:43 +09:00
|
|
|
public ByteBuf outboundByteBuffer() {
|
2012-06-07 19:39:37 +09:00
|
|
|
return nextOutboundByteBuffer(tail);
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
|
|
|
|
2012-11-16 06:04:37 +09:00
|
|
|
ByteBuf nextOutboundByteBuffer(DefaultChannelHandlerContext ctx) {
|
2012-08-20 21:03:23 +09:00
|
|
|
final DefaultChannelHandlerContext initialCtx = ctx;
|
2012-06-07 19:39:37 +09:00
|
|
|
final Thread currentThread = Thread.currentThread();
|
2012-06-01 17:51:19 -07:00
|
|
|
for (;;) {
|
|
|
|
if (ctx == null) {
|
2012-11-15 16:42:21 +08:00
|
|
|
if (initialCtx != null && initialCtx.next != null) {
|
2012-08-20 21:03:23 +09:00
|
|
|
throw new NoSuchBufferException(String.format(
|
|
|
|
"the handler '%s' could not find a %s whose outbound buffer is %s.",
|
|
|
|
initialCtx.next.name(), ChannelOutboundHandler.class.getSimpleName(),
|
|
|
|
ByteBuf.class.getSimpleName()));
|
|
|
|
} else {
|
|
|
|
throw new NoSuchBufferException(String.format(
|
|
|
|
"the pipeline does not contain a %s whose outbound buffer is %s.",
|
|
|
|
ChannelOutboundHandler.class.getSimpleName(),
|
|
|
|
ByteBuf.class.getSimpleName()));
|
|
|
|
}
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
|
|
|
|
2012-11-16 06:04:37 +09:00
|
|
|
if (ctx.hasOutboundByteBuffer()) {
|
2012-06-07 19:39:37 +09:00
|
|
|
if (ctx.executor().inEventLoop(currentThread)) {
|
2012-11-16 06:04:37 +09:00
|
|
|
return ctx.outboundByteBuffer();
|
2012-06-04 00:24:34 -07:00
|
|
|
} else {
|
2012-06-09 21:05:59 +09:00
|
|
|
ByteBridge bridge = ctx.outByteBridge.get();
|
2012-06-04 00:24:34 -07:00
|
|
|
if (bridge == null) {
|
2012-11-16 06:04:37 +09:00
|
|
|
bridge = new ByteBridge(ctx);
|
2012-06-04 00:24:34 -07:00
|
|
|
if (!ctx.outByteBridge.compareAndSet(null, bridge)) {
|
|
|
|
bridge = ctx.outByteBridge.get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bridge.byteBuf;
|
|
|
|
}
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
|
|
|
ctx = ctx.prev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-16 06:04:37 +09:00
|
|
|
MessageBuf<Object> nextOutboundMessageBuffer(DefaultChannelHandlerContext ctx) {
|
2012-08-20 21:03:23 +09:00
|
|
|
final DefaultChannelHandlerContext initialCtx = ctx;
|
2012-06-07 19:39:37 +09:00
|
|
|
final Thread currentThread = Thread.currentThread();
|
2012-06-01 17:51:19 -07:00
|
|
|
for (;;) {
|
|
|
|
if (ctx == null) {
|
2012-08-20 21:03:23 +09:00
|
|
|
if (initialCtx.next != null) {
|
|
|
|
throw new NoSuchBufferException(String.format(
|
|
|
|
"the handler '%s' could not find a %s whose outbound buffer is %s.",
|
|
|
|
initialCtx.next.name(), ChannelOutboundHandler.class.getSimpleName(),
|
|
|
|
MessageBuf.class.getSimpleName()));
|
|
|
|
} else {
|
|
|
|
throw new NoSuchBufferException(String.format(
|
|
|
|
"the pipeline does not contain a %s whose outbound buffer is %s.",
|
|
|
|
ChannelOutboundHandler.class.getSimpleName(),
|
|
|
|
MessageBuf.class.getSimpleName()));
|
|
|
|
}
|
2012-06-03 18:51:42 -07:00
|
|
|
}
|
|
|
|
|
2012-11-16 06:04:37 +09:00
|
|
|
if (ctx.hasOutboundMessageBuffer()) {
|
2012-06-07 19:39:37 +09:00
|
|
|
if (ctx.executor().inEventLoop(currentThread)) {
|
2012-11-16 06:04:37 +09:00
|
|
|
return ctx.outboundMessageBuffer();
|
2012-06-01 17:51:19 -07:00
|
|
|
} else {
|
2012-06-04 00:24:34 -07:00
|
|
|
MessageBridge bridge = ctx.outMsgBridge.get();
|
|
|
|
if (bridge == null) {
|
|
|
|
bridge = new MessageBridge();
|
|
|
|
if (!ctx.outMsgBridge.compareAndSet(null, bridge)) {
|
|
|
|
bridge = ctx.outMsgBridge.get();
|
2012-06-03 18:51:42 -07:00
|
|
|
}
|
|
|
|
}
|
2012-06-04 00:24:34 -07:00
|
|
|
return bridge.msgBuf;
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ctx = ctx.prev;
|
2012-05-16 23:02:06 +09:00
|
|
|
}
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void fireChannelRegistered() {
|
2012-06-08 23:11:15 +09:00
|
|
|
head.fireChannelRegistered();
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void fireChannelUnregistered() {
|
2012-06-08 23:11:15 +09:00
|
|
|
head.fireChannelUnregistered();
|
2012-11-16 06:04:37 +09:00
|
|
|
|
|
|
|
// Free all buffers if channel is closed and unregistered.
|
|
|
|
if (!channel.isOpen()) {
|
|
|
|
head.callFreeInboundBuffer();
|
|
|
|
}
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void fireChannelActive() {
|
2012-06-08 23:11:15 +09:00
|
|
|
firedChannelActive = true;
|
|
|
|
head.fireChannelActive();
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
|
|
|
|
if (channel.config().isAutoRead()) {
|
|
|
|
channel.read();
|
|
|
|
}
|
|
|
|
|
2012-06-08 23:11:15 +09:00
|
|
|
if (fireInboundBufferUpdatedOnActivation) {
|
|
|
|
fireInboundBufferUpdatedOnActivation = false;
|
|
|
|
head.fireInboundBufferUpdated();
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
}
|
2012-06-01 17:51:19 -07:00
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
@Override
|
|
|
|
public void fireChannelInactive() {
|
2012-06-08 23:11:15 +09:00
|
|
|
// Some implementations such as EmbeddedChannel can trigger inboundBufferUpdated()
|
|
|
|
// after deactivation, so it's safe not to revert the firedChannelActive flag here.
|
|
|
|
// Also, all known transports never get re-activated.
|
|
|
|
//firedChannelActive = false;
|
|
|
|
head.fireChannelInactive();
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void fireExceptionCaught(Throwable cause) {
|
2012-06-08 23:11:15 +09:00
|
|
|
head.fireExceptionCaught(cause);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
@Override
|
|
|
|
public void fireUserEventTriggered(Object event) {
|
2012-06-08 23:11:15 +09:00
|
|
|
head.fireUserEventTriggered(event);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 09:45:39 +09:00
|
|
|
@Override
|
2012-05-01 17:19:41 +09:00
|
|
|
public void fireInboundBufferUpdated() {
|
2012-05-30 11:32:39 -07:00
|
|
|
if (!firedChannelActive) {
|
|
|
|
fireInboundBufferUpdatedOnActivation = true;
|
|
|
|
return;
|
|
|
|
}
|
2012-06-08 23:11:15 +09:00
|
|
|
head.fireInboundBufferUpdated();
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
@Override
|
|
|
|
public void fireInboundBufferSuspended() {
|
|
|
|
head.fireInboundBufferSuspended();
|
|
|
|
if (channel.config().isAutoRead()) {
|
|
|
|
channel.read();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture bind(SocketAddress localAddress) {
|
2012-06-03 04:25:03 -07:00
|
|
|
return bind(localAddress, channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture connect(SocketAddress remoteAddress) {
|
2012-06-03 04:25:03 -07:00
|
|
|
return connect(remoteAddress, channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
2012-06-03 04:25:03 -07:00
|
|
|
return connect(remoteAddress, localAddress, channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture disconnect() {
|
2012-06-03 04:25:03 -07:00
|
|
|
return disconnect(channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture close() {
|
2012-06-03 04:25:03 -07:00
|
|
|
return close(channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture deregister() {
|
2012-06-03 04:25:03 -07:00
|
|
|
return deregister(channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture flush() {
|
2012-06-03 04:25:03 -07:00
|
|
|
return flush(channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture write(Object message) {
|
2012-06-03 04:25:03 -07:00
|
|
|
return write(message, channel.newFuture());
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture bind(SocketAddress localAddress, ChannelFuture future) {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
return bind(lastContext(FLAG_OPERATION_HANDLER), localAddress, future);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
2012-06-08 19:28:12 +09:00
|
|
|
ChannelFuture bind(
|
|
|
|
final DefaultChannelHandlerContext ctx, final SocketAddress localAddress, final ChannelFuture future) {
|
2012-05-01 17:19:41 +09:00
|
|
|
if (localAddress == null) {
|
|
|
|
throw new NullPointerException("localAddress");
|
|
|
|
}
|
2012-05-11 09:00:35 +09:00
|
|
|
validateFuture(future);
|
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
try {
|
2012-06-07 14:52:33 +09:00
|
|
|
((ChannelOperationHandler) ctx.handler()).bind(ctx, localAddress, future);
|
2012-06-03 18:51:42 -07:00
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
} else {
|
2012-06-03 18:51:42 -07:00
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
bind(ctx, localAddress, future);
|
|
|
|
}
|
|
|
|
});
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2012-05-09 22:09:06 +09:00
|
|
|
return future;
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture connect(SocketAddress remoteAddress, ChannelFuture future) {
|
|
|
|
return connect(remoteAddress, null, future);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
return connect(lastContext(FLAG_OPERATION_HANDLER), remoteAddress, localAddress, future);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
2012-06-08 19:28:12 +09:00
|
|
|
ChannelFuture connect(
|
|
|
|
final DefaultChannelHandlerContext ctx, final SocketAddress remoteAddress,
|
|
|
|
final SocketAddress localAddress, final ChannelFuture future) {
|
2012-05-01 17:19:41 +09:00
|
|
|
if (remoteAddress == null) {
|
|
|
|
throw new NullPointerException("remoteAddress");
|
|
|
|
}
|
2012-05-11 09:00:35 +09:00
|
|
|
validateFuture(future);
|
2012-05-01 17:19:41 +09:00
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
try {
|
2012-06-07 14:52:33 +09:00
|
|
|
((ChannelOperationHandler) ctx.handler()).connect(ctx, remoteAddress, localAddress, future);
|
2012-06-03 18:51:42 -07:00
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
} else {
|
2012-06-03 18:51:42 -07:00
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
connect(ctx, remoteAddress, localAddress, future);
|
|
|
|
}
|
|
|
|
});
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2012-05-09 22:09:06 +09:00
|
|
|
|
|
|
|
return future;
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture disconnect(ChannelFuture future) {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
return disconnect(lastContext(FLAG_OPERATION_HANDLER), future);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
2012-06-01 17:51:19 -07:00
|
|
|
ChannelFuture disconnect(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
|
2012-06-19 10:39:30 +09:00
|
|
|
// Translate disconnect to close if the channel has no notion of disconnect-reconnect.
|
|
|
|
// So far, UDP/IP is the only transport that has such behavior.
|
|
|
|
if (!ctx.channel().metadata().hasDisconnect()) {
|
|
|
|
return close(ctx, future);
|
|
|
|
}
|
|
|
|
|
2012-05-11 09:00:35 +09:00
|
|
|
validateFuture(future);
|
2012-06-03 18:51:42 -07:00
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
try {
|
2012-06-07 14:52:33 +09:00
|
|
|
((ChannelOperationHandler) ctx.handler()).disconnect(ctx, future);
|
2012-06-03 18:51:42 -07:00
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
} else {
|
2012-06-03 18:51:42 -07:00
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
disconnect(ctx, future);
|
|
|
|
}
|
|
|
|
});
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2012-05-09 22:09:06 +09:00
|
|
|
|
|
|
|
return future;
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture close(ChannelFuture future) {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
return close(lastContext(FLAG_OPERATION_HANDLER), future);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
2012-06-01 17:51:19 -07:00
|
|
|
ChannelFuture close(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
|
2012-05-11 09:00:35 +09:00
|
|
|
validateFuture(future);
|
2012-06-03 18:51:42 -07:00
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
try {
|
2012-06-07 14:52:33 +09:00
|
|
|
((ChannelOperationHandler) ctx.handler()).close(ctx, future);
|
2012-06-03 18:51:42 -07:00
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-05-01 17:19:41 +09:00
|
|
|
} else {
|
2012-06-03 18:51:42 -07:00
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
close(ctx, future);
|
|
|
|
}
|
|
|
|
});
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
2012-05-09 22:09:06 +09:00
|
|
|
|
|
|
|
return future;
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture deregister(final ChannelFuture future) {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
return deregister(lastContext(FLAG_OPERATION_HANDLER), future);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2012-06-01 17:51:19 -07:00
|
|
|
ChannelFuture deregister(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
|
2012-05-11 09:00:35 +09:00
|
|
|
validateFuture(future);
|
2012-06-03 18:51:42 -07:00
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
try {
|
2012-06-07 14:52:33 +09:00
|
|
|
((ChannelOperationHandler) ctx.handler()).deregister(ctx, future);
|
2012-06-03 18:51:42 -07:00
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
} else {
|
2012-06-03 18:51:42 -07:00
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
deregister(ctx, future);
|
|
|
|
}
|
|
|
|
});
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2012-05-09 22:09:06 +09:00
|
|
|
|
|
|
|
return future;
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
2012-10-24 18:27:26 +02:00
|
|
|
@Override
|
|
|
|
public ChannelFuture sendFile(FileRegion region) {
|
|
|
|
return sendFile(region, channel().newFuture());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public ChannelFuture sendFile(FileRegion region, ChannelFuture future) {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
return sendFile(lastContext(FLAG_OPERATION_HANDLER), region, future);
|
2012-10-24 18:27:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ChannelFuture sendFile(final DefaultChannelHandlerContext ctx, final FileRegion region,
|
|
|
|
final ChannelFuture future) {
|
2012-12-18 02:58:36 +09:00
|
|
|
if (region == null) {
|
|
|
|
throw new NullPointerException("region");
|
|
|
|
}
|
2012-10-24 18:27:26 +02:00
|
|
|
validateFuture(future);
|
2012-12-18 02:58:36 +09:00
|
|
|
|
2012-10-24 18:27:26 +02:00
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
try {
|
|
|
|
ctx.flushBridge();
|
|
|
|
((ChannelOperationHandler) ctx.handler()).sendFile(ctx, region, future);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
sendFile(ctx, region, future);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return future;
|
|
|
|
}
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void read() {
|
|
|
|
read(lastContext(FLAG_OPERATION_HANDLER));
|
|
|
|
}
|
|
|
|
|
|
|
|
void read(final DefaultChannelHandlerContext ctx) {
|
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
read0(ctx);
|
|
|
|
} else {
|
|
|
|
executor.execute(ctx.read0Task);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void read0(DefaultChannelHandlerContext ctx) {
|
|
|
|
try {
|
|
|
|
((ChannelOperationHandler) ctx.handler()).read(ctx);
|
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture flush(ChannelFuture future) {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
return flush(lastContext(FLAG_OPERATION_HANDLER), future);
|
2012-05-09 22:09:06 +09:00
|
|
|
}
|
|
|
|
|
2012-06-01 17:51:19 -07:00
|
|
|
ChannelFuture flush(final DefaultChannelHandlerContext ctx, final ChannelFuture future) {
|
2012-05-11 09:00:35 +09:00
|
|
|
validateFuture(future);
|
2012-06-03 18:51:42 -07:00
|
|
|
EventExecutor executor = ctx.executor();
|
|
|
|
if (executor.inEventLoop()) {
|
|
|
|
flush0(ctx, future);
|
2012-05-01 17:19:41 +09:00
|
|
|
} else {
|
2012-06-03 18:51:42 -07:00
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
flush0(ctx, future);
|
2012-06-03 18:51:42 -07:00
|
|
|
}
|
|
|
|
});
|
2011-08-01 04:16:02 +09:00
|
|
|
}
|
2012-05-01 17:19:41 +09:00
|
|
|
|
2012-05-09 22:09:06 +09:00
|
|
|
return future;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
2012-06-01 18:34:19 -07:00
|
|
|
private void flush0(final DefaultChannelHandlerContext ctx, ChannelFuture future) {
|
2012-12-18 04:52:46 +09:00
|
|
|
if (!channel.isRegistered() && !channel.isActive()) {
|
|
|
|
future.setFailure(new ClosedChannelException());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-01 18:34:19 -07:00
|
|
|
try {
|
2012-06-03 18:51:42 -07:00
|
|
|
ctx.flushBridge();
|
2012-06-07 16:56:21 +09:00
|
|
|
((ChannelOperationHandler) ctx.handler()).flush(ctx, future);
|
2012-06-01 18:34:19 -07:00
|
|
|
} catch (Throwable t) {
|
|
|
|
notifyHandlerException(t);
|
|
|
|
} finally {
|
2012-11-16 06:04:37 +09:00
|
|
|
if (ctx.hasOutboundByteBuffer()) {
|
|
|
|
ByteBuf buf = ctx.outboundByteBuffer();
|
2012-06-04 11:56:00 -07:00
|
|
|
if (!buf.readable()) {
|
|
|
|
buf.discardReadBytes();
|
|
|
|
}
|
2012-06-01 18:34:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
@Override
|
2012-05-09 22:09:06 +09:00
|
|
|
public ChannelFuture write(Object message, ChannelFuture future) {
|
2012-10-24 18:27:26 +02:00
|
|
|
if (message instanceof FileRegion) {
|
|
|
|
return sendFile((FileRegion) message, future);
|
|
|
|
}
|
2012-06-03 04:25:03 -07:00
|
|
|
return write(tail, message, future);
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
|
|
|
|
2012-06-03 04:10:32 -07:00
|
|
|
ChannelFuture write(DefaultChannelHandlerContext ctx, final Object message, final ChannelFuture future) {
|
2012-05-11 09:00:35 +09:00
|
|
|
if (message == null) {
|
|
|
|
throw new NullPointerException("message");
|
|
|
|
}
|
|
|
|
validateFuture(future);
|
|
|
|
|
2012-08-20 21:03:23 +09:00
|
|
|
final DefaultChannelHandlerContext initialCtx = ctx;
|
2012-06-01 17:51:19 -07:00
|
|
|
EventExecutor executor;
|
2012-06-03 04:10:32 -07:00
|
|
|
boolean msgBuf = false;
|
|
|
|
for (;;) {
|
|
|
|
if (ctx == null) {
|
2012-08-20 21:03:23 +09:00
|
|
|
if (initialCtx.next != null) {
|
|
|
|
throw new NoSuchBufferException(String.format(
|
|
|
|
"the handler '%s' could not find a %s which accepts a %s, and " +
|
|
|
|
"the transport does not accept it as-is.",
|
|
|
|
initialCtx.next.name(),
|
|
|
|
ChannelOutboundHandler.class.getSimpleName(),
|
|
|
|
message.getClass().getSimpleName()));
|
|
|
|
} else {
|
|
|
|
throw new NoSuchBufferException(String.format(
|
|
|
|
"the pipeline does not contain a %s which accepts a %s, and " +
|
|
|
|
"the transport does not accept it as-is.",
|
|
|
|
ChannelOutboundHandler.class.getSimpleName(),
|
|
|
|
message.getClass().getSimpleName()));
|
|
|
|
}
|
2012-06-03 04:10:32 -07:00
|
|
|
}
|
|
|
|
|
2012-06-07 14:52:33 +09:00
|
|
|
if (ctx.hasOutboundMessageBuffer()) {
|
|
|
|
msgBuf = true;
|
|
|
|
executor = ctx.executor();
|
|
|
|
break;
|
2012-06-07 22:01:59 +09:00
|
|
|
}
|
|
|
|
|
2012-06-10 11:08:43 +09:00
|
|
|
if (message instanceof ByteBuf && ctx.hasOutboundByteBuffer()) {
|
2012-06-07 14:52:33 +09:00
|
|
|
executor = ctx.executor();
|
|
|
|
break;
|
2012-06-03 04:10:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx = ctx.prev;
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2012-05-16 23:02:06 +09:00
|
|
|
|
2012-06-01 17:51:19 -07:00
|
|
|
if (executor.inEventLoop()) {
|
2012-12-18 02:58:36 +09:00
|
|
|
write0(ctx, message, future, msgBuf);
|
2012-06-01 18:34:19 -07:00
|
|
|
return future;
|
2012-06-01 17:51:19 -07:00
|
|
|
}
|
2012-11-12 09:50:10 +09:00
|
|
|
|
2012-12-18 02:58:36 +09:00
|
|
|
final boolean msgBuf0 = msgBuf;
|
2012-11-12 09:31:40 +09:00
|
|
|
final DefaultChannelHandlerContext ctx0 = ctx;
|
|
|
|
executor.execute(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2012-12-18 02:58:36 +09:00
|
|
|
write0(ctx0, message, future, msgBuf0);
|
2012-11-12 09:31:40 +09:00
|
|
|
}
|
|
|
|
});
|
2012-06-01 17:51:19 -07:00
|
|
|
|
|
|
|
return future;
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
|
|
|
|
2012-12-18 02:58:36 +09:00
|
|
|
private void write0(DefaultChannelHandlerContext ctx, Object message, ChannelFuture future, boolean msgBuf) {
|
2012-12-18 04:52:46 +09:00
|
|
|
if (!channel.isRegistered() && !channel.isActive()) {
|
|
|
|
future.setFailure(new ClosedChannelException());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-18 02:58:36 +09:00
|
|
|
if (msgBuf) {
|
2012-12-18 04:52:46 +09:00
|
|
|
ctx.outboundMessageBuffer().add(message);
|
2012-12-18 02:58:36 +09:00
|
|
|
} else {
|
2012-12-18 04:52:46 +09:00
|
|
|
ByteBuf buf = (ByteBuf) message;
|
|
|
|
ctx.outboundByteBuffer().writeBytes(buf, buf.readerIndex(), buf.readableBytes());
|
2012-12-18 02:58:36 +09:00
|
|
|
}
|
|
|
|
flush0(ctx, future);
|
|
|
|
}
|
|
|
|
|
2012-05-11 09:00:35 +09:00
|
|
|
private void validateFuture(ChannelFuture future) {
|
|
|
|
if (future == null) {
|
|
|
|
throw new NullPointerException("future");
|
|
|
|
}
|
2012-06-03 04:25:03 -07:00
|
|
|
if (future.channel() != channel) {
|
2012-05-11 09:00:35 +09:00
|
|
|
throw new IllegalArgumentException(String.format(
|
2012-06-03 04:25:03 -07:00
|
|
|
"future.channel does not match: %s (expected: %s)", future.channel(), channel));
|
2012-05-11 09:00:35 +09:00
|
|
|
}
|
2012-05-13 01:35:43 +09:00
|
|
|
if (future.isDone()) {
|
|
|
|
throw new IllegalArgumentException("future already done");
|
|
|
|
}
|
|
|
|
if (future instanceof ChannelFuture.Unsafe) {
|
|
|
|
throw new IllegalArgumentException("internal use only future not allowed");
|
|
|
|
}
|
2012-05-11 09:00:35 +09:00
|
|
|
}
|
|
|
|
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
DefaultChannelHandlerContext lastContext(int flag) {
|
|
|
|
return prevContext(tail, flag);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
static DefaultChannelHandlerContext nextContext(DefaultChannelHandlerContext ctx, int flag) {
|
2008-08-08 00:37:18 +00:00
|
|
|
if (ctx == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
DefaultChannelHandlerContext realCtx = ctx;
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
while ((realCtx.flags & flag) == 0) {
|
|
|
|
realCtx = realCtx.next;
|
|
|
|
if (realCtx == null) {
|
|
|
|
return null;
|
2012-06-07 14:52:33 +09:00
|
|
|
}
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
}
|
|
|
|
return realCtx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DefaultChannelHandlerContext prevContext(DefaultChannelHandlerContext ctx, int flag) {
|
|
|
|
if (ctx == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
DefaultChannelHandlerContext realCtx = ctx;
|
|
|
|
while ((realCtx.flags & flag) == 0) {
|
|
|
|
realCtx = realCtx.prev;
|
|
|
|
if (realCtx == null) {
|
|
|
|
return null;
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return realCtx;
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
protected void notifyHandlerException(Throwable cause) {
|
|
|
|
if (!(cause instanceof ChannelPipelineException)) {
|
|
|
|
cause = new ChannelPipelineException(cause);
|
|
|
|
}
|
2012-02-29 13:53:26 -08:00
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
if (inExceptionCaught(cause)) {
|
2012-02-17 10:37:41 +01:00
|
|
|
if (logger.isWarnEnabled()) {
|
|
|
|
logger.warn(
|
|
|
|
"An exception was thrown by a user handler " +
|
2012-05-01 17:19:41 +09:00
|
|
|
"while handling an exceptionCaught event", cause);
|
2012-02-17 10:37:41 +01:00
|
|
|
}
|
2008-08-18 11:11:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-05-01 17:19:41 +09:00
|
|
|
fireExceptionCaught(cause);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean inExceptionCaught(Throwable cause) {
|
2012-11-12 13:14:24 +09:00
|
|
|
for (;;) {
|
|
|
|
if (cause == null) {
|
|
|
|
return false;
|
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
|
2012-11-12 13:14:24 +09:00
|
|
|
StackTraceElement[] trace = cause.getStackTrace();
|
|
|
|
if (trace != null) {
|
|
|
|
for (StackTraceElement t : trace) {
|
|
|
|
if ("exceptionCaught".equals(t.getMethodName())) {
|
|
|
|
return true;
|
|
|
|
}
|
2012-05-01 17:19:41 +09:00
|
|
|
}
|
2012-02-17 10:37:41 +01:00
|
|
|
}
|
2012-05-01 17:19:41 +09:00
|
|
|
|
2012-11-12 13:14:24 +09:00
|
|
|
cause = cause.getCause();
|
|
|
|
}
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void checkDuplicateName(String name) {
|
|
|
|
if (name2ctx.containsKey(name)) {
|
2011-11-23 14:07:26 +09:00
|
|
|
throw new IllegalArgumentException("Duplicate handler name: " + name);
|
2008-08-08 00:37:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private DefaultChannelHandlerContext getContextOrDie(String name) {
|
2012-05-01 17:19:41 +09:00
|
|
|
DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(name);
|
2012-06-03 18:51:42 -07:00
|
|
|
if (ctx == null || ctx == head) {
|
2008-08-08 00:37:18 +00:00
|
|
|
throw new NoSuchElementException(name);
|
|
|
|
} else {
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private DefaultChannelHandlerContext getContextOrDie(ChannelHandler handler) {
|
2012-05-01 17:19:41 +09:00
|
|
|
DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(handler);
|
2012-06-03 18:51:42 -07:00
|
|
|
if (ctx == null || ctx == head) {
|
2008-08-08 00:37:18 +00:00
|
|
|
throw new NoSuchElementException(handler.getClass().getName());
|
|
|
|
} else {
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private DefaultChannelHandlerContext getContextOrDie(Class<? extends ChannelHandler> handlerType) {
|
2012-05-01 17:19:41 +09:00
|
|
|
DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) context(handlerType);
|
2012-06-03 18:51:42 -07:00
|
|
|
if (ctx == null || ctx == head) {
|
2008-08-08 00:37:18 +00:00
|
|
|
throw new NoSuchElementException(handlerType.getName());
|
|
|
|
} else {
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
}
|
2012-06-03 18:51:42 -07:00
|
|
|
|
2012-06-07 19:39:37 +09:00
|
|
|
private final class HeadHandler implements ChannelOutboundHandler {
|
2012-06-03 18:51:42 -07:00
|
|
|
@Override
|
2012-12-17 17:43:45 +09:00
|
|
|
public Buf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception {
|
2012-06-19 10:39:30 +09:00
|
|
|
switch (channel.metadata().bufferType()) {
|
2012-06-09 21:05:59 +09:00
|
|
|
case BYTE:
|
2012-11-16 06:04:37 +09:00
|
|
|
return ctx.alloc().ioBuffer();
|
2012-06-03 18:51:42 -07:00
|
|
|
case MESSAGE:
|
2012-06-11 17:02:00 +09:00
|
|
|
return Unpooled.messageBuffer();
|
2012-06-03 18:51:42 -07:00
|
|
|
default:
|
|
|
|
throw new Error();
|
|
|
|
}
|
2012-11-16 06:04:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-12-17 17:43:45 +09:00
|
|
|
public void freeOutboundBuffer(ChannelHandlerContext ctx, Buf buf) {
|
2012-12-17 17:41:21 +09:00
|
|
|
buf.free();
|
2012-06-03 18:51:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@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
|
2012-06-07 14:52:33 +09:00
|
|
|
public void bind(
|
|
|
|
ChannelHandlerContext ctx, SocketAddress localAddress, ChannelFuture future)
|
2012-06-03 18:51:42 -07:00
|
|
|
throws Exception {
|
|
|
|
unsafe.bind(localAddress, future);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-07 14:52:33 +09:00
|
|
|
public void connect(
|
|
|
|
ChannelHandlerContext ctx,
|
2012-06-03 18:51:42 -07:00
|
|
|
SocketAddress remoteAddress, SocketAddress localAddress,
|
|
|
|
ChannelFuture future) throws Exception {
|
|
|
|
unsafe.connect(remoteAddress, localAddress, future);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-07 14:52:33 +09:00
|
|
|
public void disconnect(ChannelHandlerContext ctx, ChannelFuture future) throws Exception {
|
2012-06-03 18:51:42 -07:00
|
|
|
unsafe.disconnect(future);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-07 14:52:33 +09:00
|
|
|
public void close(ChannelHandlerContext ctx, ChannelFuture future) throws Exception {
|
2012-06-03 18:51:42 -07:00
|
|
|
unsafe.close(future);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2012-06-07 14:52:33 +09:00
|
|
|
public void deregister(ChannelHandlerContext ctx, ChannelFuture future) throws Exception {
|
2012-06-03 18:51:42 -07:00
|
|
|
unsafe.deregister(future);
|
|
|
|
}
|
|
|
|
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 21:53:59 +09:00
|
|
|
@Override
|
|
|
|
public void read(ChannelHandlerContext ctx) {
|
|
|
|
unsafe.beginRead();
|
|
|
|
}
|
|
|
|
|
2012-06-03 18:51:42 -07:00
|
|
|
@Override
|
2012-06-07 14:52:33 +09:00
|
|
|
public void flush(ChannelHandlerContext ctx, ChannelFuture future) throws Exception {
|
2012-06-03 18:51:42 -07:00
|
|
|
unsafe.flush(future);
|
|
|
|
}
|
2012-06-07 14:52:33 +09:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
|
|
ctx.fireExceptionCaught(cause);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
|
|
|
ctx.fireUserEventTriggered(evt);
|
|
|
|
}
|
2012-10-24 18:27:26 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelFuture future) throws Exception {
|
|
|
|
unsafe.sendFile(region, future);
|
|
|
|
}
|
2012-06-03 18:51:42 -07:00
|
|
|
}
|
2012-06-08 10:57:38 +09:00
|
|
|
}
|