Tighten contract between Channel and EventLoop by require the EventLoop on Channel construction. (#8587)

Motivation:

At the moment it’s possible to have a Channel in Netty that is not registered / assigned to an EventLoop until register(...) is called. This is suboptimal as if the Channel is not registered it is also not possible to do anything useful with a ChannelFuture that belongs to the Channel. We should think about if we should have the EventLoop as a constructor argument of a Channel and have the register / deregister method only have the effect of add a Channel to KQueue/Epoll/... It is also currently possible to deregister a Channel from one EventLoop and register it with another EventLoop. This operation defeats the threading model assumptions that are wide spread in Netty, and requires careful user level coordination to pull off without any concurrency issues. It is not a commonly used feature in practice, may be better handled by other means (e.g. client side load balancing), and therefore we propose removing this feature.

Modifications:

- Change all Channel implementations to require an EventLoop for construction ( + an EventLoopGroup for all ServerChannel implementations)
- Remove all register(...) methods from EventLoopGroup
- Add ChannelOutboundInvoker.register(...) which now basically means we want to register on the EventLoop for IO.
- Change ChannelUnsafe.register(...) to not take an EventLoop as parameter (as the EventLoop is supplied on custruction).
- Change ChannelFactory to take an EventLoop to create new Channels and introduce ServerChannelFactory which takes an EventLoop and one EventLoopGroup to create new ServerChannel instances.
- Add ServerChannel.childEventLoopGroup()
- Ensure all operations on the accepted Channel is done in the EventLoop of the Channel in ServerBootstrap
- Change unit tests for new behaviour

Result:

A Channel always has an EventLoop assigned which will never change during its life-time. This ensures we are always be able to call any operation on the Channel once constructed (unit the EventLoop is shutdown). This also simplifies the logic in DefaultChannelPipeline a lot as we can always call handlerAdded / handlerRemoved directly without the need to wait for register() to happen.

Also note that its still possible to deregister a Channel and register it again. It's just not possible anymore to move from one EventLoop to another (which was not really safe anyway).

Fixes https://github.com/netty/netty/issues/8513.
This commit is contained in:
Norman Maurer 2019-01-14 20:11:13 +01:00 committed by GitHub
parent 13f640cbba
commit c10ccc5dec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 1118 additions and 1108 deletions

View File

@ -146,6 +146,11 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator implements Ch
ctx.close(promise);
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.deregister(promise);

View File

@ -155,6 +155,11 @@ public class SpdyFrameCodec extends ByteToMessageDecoder
ctx.close(promise);
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.deregister(promise);

View File

@ -500,6 +500,11 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
}
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.deregister(promise);

View File

@ -256,7 +256,7 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
// Add our upgrade handler to the channel and then register the channel.
// The register call fires the channelActive, etc.
ch.pipeline().addLast(upgradeStreamHandler);
ChannelFuture future = ctx.channel().eventLoop().register(ch);
ChannelFuture future = ch.register();
if (future.isDone()) {
registerDone(future);
} else {
@ -276,7 +276,7 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
break;
}
// fall-trough
ChannelFuture future = ctx.channel().eventLoop().register(new DefaultHttp2StreamChannel(s, false));
ChannelFuture future = new DefaultHttp2StreamChannel(s, false).register();
if (future.isDone()) {
registerDone(future);
} else {
@ -641,6 +641,11 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
return pipeline().close();
}
@Override
public ChannelFuture register() {
return register(newPromise());
}
@Override
public ChannelFuture deregister() {
return pipeline().deregister();
@ -671,6 +676,11 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
return pipeline().close(promise);
}
@Override
public ChannelFuture register(ChannelPromise promise) {
return pipeline().register(promise);
}
@Override
public ChannelFuture deregister(ChannelPromise promise) {
return pipeline().deregister(promise);
@ -830,7 +840,7 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
}
@Override
public void register(EventLoop eventLoop, ChannelPromise promise) {
public void register(ChannelPromise promise) {
if (!promise.setUncancellable()) {
return;
}

View File

@ -140,7 +140,7 @@ public final class Http2StreamChannelBootstrap {
return;
}
ChannelFuture future = ctx.channel().eventLoop().register(streamChannel);
ChannelFuture future = streamChannel.register();
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {

View File

@ -250,6 +250,11 @@ final class Http2FrameInboundWriter {
return channel.close();
}
@Override
public ChannelFuture register() {
return channel.register();
}
@Override
public ChannelFuture deregister() {
return channel.deregister();
@ -280,6 +285,11 @@ final class Http2FrameInboundWriter {
return channel.close(promise);
}
@Override
public ChannelFuture register(ChannelPromise promise) {
return channel.register(promise);
}
@Override
public ChannelFuture deregister(ChannelPromise promise) {
return channel.deregister(promise);

View File

@ -310,6 +310,11 @@ public abstract class AbstractSniHandler<T> extends ByteToMessageDecoder impleme
ctx.close(promise);
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.deregister(promise);

View File

@ -716,6 +716,11 @@ public class SslHandler extends ByteToMessageDecoder implements ChannelOutboundH
ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.deregister(promise);

View File

@ -162,6 +162,22 @@ public abstract class EmbeddedChannelHandlerContext implements ChannelHandlerCon
return close(newPromise());
}
@Override
public ChannelFuture register() {
return register(newPromise());
}
@Override
public ChannelFuture register(ChannelPromise promise) {
try {
channel().register(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture deregister() {
return deregister(newPromise());

View File

@ -2326,7 +2326,7 @@ public class DnsNameResolverTest {
try {
newResolver().channelFactory(new ChannelFactory<DatagramChannel>() {
@Override
public DatagramChannel newChannel() {
public DatagramChannel newChannel(EventLoop eventLoop) {
throw exception;
}
}).build();

View File

@ -28,8 +28,8 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public abstract class AbstractComboTestsuiteTest<SB extends AbstractBootstrap<?, ?>,
CB extends AbstractBootstrap<?, ?>> {
public abstract class AbstractComboTestsuiteTest<SB extends AbstractBootstrap<?, ?, ?>,
CB extends AbstractBootstrap<?, ?, ?>> {
private final Class<SB> sbClazz;
private final Class<CB> cbClazz;
protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());

View File

@ -28,7 +28,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public abstract class AbstractTestsuiteTest<T extends AbstractBootstrap<?, ?>> {
public abstract class AbstractTestsuiteTest<T extends AbstractBootstrap<?, ?, ?>> {
private final Class<T> clazz;
protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass());
protected volatile T cb;

View File

@ -34,11 +34,12 @@ public final class TestsuitePermutation {
private TestsuitePermutation() { }
public interface BootstrapFactory<CB extends AbstractBootstrap<?, ?>> {
public interface BootstrapFactory<CB extends AbstractBootstrap<?, ?, ?>> {
CB newInstance();
}
public interface BootstrapComboFactory<SB extends AbstractBootstrap<?, ?>, CB extends AbstractBootstrap<?, ?>> {
public interface BootstrapComboFactory<SB extends AbstractBootstrap<?, ?, ?>,
CB extends AbstractBootstrap<?, ?, ?>> {
SB newServerInstance();
CB newClientInstance();
}

View File

@ -34,9 +34,15 @@ public class SocketCloseForciblyTest extends AbstractSocketTest {
sb.handler(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
SocketChannel childChannel = (SocketChannel) msg;
childChannel.config().setSoLinger(0);
childChannel.unsafe().closeForcibly();
final SocketChannel childChannel = (SocketChannel) msg;
// Dispatch on the EventLoop as all operation on Unsafe should be done while on the EventLoop.
childChannel.eventLoop().execute(new Runnable() {
@Override
public void run() {
childChannel.config().setSoLinger(0);
childChannel.unsafe().closeForcibly();
}
});
}
}).childHandler(new ChannelInboundHandlerAdapter());

View File

@ -20,6 +20,7 @@ import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.InternetProtocolFamily;
@ -58,7 +59,7 @@ public class SocketTestPermutation {
protected final EventLoopGroup nioWorkerGroup =
new NioEventLoopGroup(WORKERS, new DefaultThreadFactory("testsuite-nio-worker", true));
protected <A extends AbstractBootstrap<?, ?>, B extends AbstractBootstrap<?, ?>>
protected <A extends AbstractBootstrap<?, ?, ?>, B extends AbstractBootstrap<?, ?, ?>>
List<BootstrapComboFactory<A, B>> combo(List<BootstrapFactory<A>> sbfs, List<BootstrapFactory<B>> cbfs) {
List<BootstrapComboFactory<A, B>> list = new ArrayList<BootstrapComboFactory<A, B>>();
@ -104,8 +105,8 @@ public class SocketTestPermutation {
public Bootstrap newInstance() {
return new Bootstrap().group(nioWorkerGroup).channelFactory(new ChannelFactory<Channel>() {
@Override
public Channel newChannel() {
return new NioDatagramChannel(InternetProtocolFamily.IPv4);
public Channel newChannel(EventLoop eventLoop) {
return new NioDatagramChannel(eventLoop, InternetProtocolFamily.IPv4);
}
@Override

View File

@ -78,12 +78,12 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
protected volatile boolean active;
AbstractEpollChannel(LinuxSocket fd) {
this(null, fd, false);
AbstractEpollChannel(EventLoop eventLoop, LinuxSocket fd) {
this(null, eventLoop, fd, false);
}
AbstractEpollChannel(Channel parent, LinuxSocket fd, boolean active) {
super(parent);
AbstractEpollChannel(Channel parent, EventLoop eventLoop, LinuxSocket fd, boolean active) {
super(parent, eventLoop);
socket = checkNotNull(fd, "fd");
this.active = active;
if (active) {
@ -94,8 +94,8 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
}
}
AbstractEpollChannel(Channel parent, LinuxSocket fd, SocketAddress remote) {
super(parent);
AbstractEpollChannel(Channel parent, EventLoop eventLoop, LinuxSocket fd, SocketAddress remote) {
super(parent, eventLoop);
socket = checkNotNull(fd, "fd");
active = true;
// Directly cache the remote and local addresses

View File

@ -22,7 +22,9 @@ import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.util.internal.ObjectUtil;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@ -30,16 +32,25 @@ import java.net.SocketAddress;
public abstract class AbstractEpollServerChannel extends AbstractEpollChannel implements ServerChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
protected AbstractEpollServerChannel(int fd) {
this(new LinuxSocket(fd), false);
private final EventLoopGroup childEventLoopGroup;
protected AbstractEpollServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, int fd) {
this(eventLoop, childEventLoopGroup, new LinuxSocket(fd), false);
}
AbstractEpollServerChannel(LinuxSocket fd) {
this(fd, isSoErrorZero(fd));
AbstractEpollServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, LinuxSocket fd) {
this(eventLoop, childEventLoopGroup, fd, isSoErrorZero(fd));
}
AbstractEpollServerChannel(LinuxSocket fd, boolean active) {
super(null, fd, active);
AbstractEpollServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup,
LinuxSocket fd, boolean active) {
super(null, eventLoop, fd, active);
this.childEventLoopGroup = ObjectUtil.checkNotNull(childEventLoopGroup, "childEventLoopGroup");
}
@Override
public EventLoopGroup childEventLoopGroup() {
return childEventLoopGroup;
}
@Override

View File

@ -86,32 +86,32 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel im
private WritableByteChannel byteChannel;
protected AbstractEpollStreamChannel(Channel parent, int fd) {
this(parent, new LinuxSocket(fd));
protected AbstractEpollStreamChannel(Channel parent, EventLoop eventLoop, int fd) {
this(parent, eventLoop, new LinuxSocket(fd));
}
protected AbstractEpollStreamChannel(int fd) {
this(new LinuxSocket(fd));
protected AbstractEpollStreamChannel(EventLoop eventLoop, int fd) {
this(eventLoop, new LinuxSocket(fd));
}
AbstractEpollStreamChannel(LinuxSocket fd) {
this(fd, isSoErrorZero(fd));
AbstractEpollStreamChannel(EventLoop eventLoop, LinuxSocket fd) {
this(eventLoop, fd, isSoErrorZero(fd));
}
AbstractEpollStreamChannel(Channel parent, LinuxSocket fd) {
super(parent, fd, true);
AbstractEpollStreamChannel(Channel parent, EventLoop eventLoop, LinuxSocket fd) {
super(parent, eventLoop, fd, true);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP;
}
AbstractEpollStreamChannel(Channel parent, LinuxSocket fd, SocketAddress remote) {
super(parent, fd, remote);
AbstractEpollStreamChannel(Channel parent, EventLoop eventLoop, LinuxSocket fd, SocketAddress remote) {
super(parent, eventLoop, fd, remote);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP;
}
protected AbstractEpollStreamChannel(LinuxSocket fd, boolean active) {
super(null, fd, active);
protected AbstractEpollStreamChannel(EventLoop eventLoop, LinuxSocket fd, boolean active) {
super(null, eventLoop, fd, active);
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
flags |= Native.EPOLLRDHUP;
}

View File

@ -24,6 +24,7 @@ import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultAddressedEnvelope;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.DatagramChannelConfig;
import io.netty.channel.socket.DatagramPacket;
@ -58,17 +59,17 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
private final EpollDatagramChannelConfig config;
private volatile boolean connected;
public EpollDatagramChannel() {
super(newSocketDgram());
public EpollDatagramChannel(EventLoop eventLoop) {
super(eventLoop, newSocketDgram());
config = new EpollDatagramChannelConfig(this);
}
public EpollDatagramChannel(int fd) {
this(new LinuxSocket(fd));
public EpollDatagramChannel(EventLoop eventLoop, int fd) {
this(eventLoop, new LinuxSocket(fd));
}
EpollDatagramChannel(LinuxSocket fd) {
super(null, fd, true);
EpollDatagramChannel(EventLoop eventLoop, LinuxSocket fd) {
super(null, eventLoop, fd, true);
config = new EpollDatagramChannelConfig(this);
}

View File

@ -19,6 +19,7 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.DomainSocketChannel;
import io.netty.channel.unix.FileDescriptor;
@ -36,24 +37,24 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
private volatile DomainSocketAddress local;
private volatile DomainSocketAddress remote;
public EpollDomainSocketChannel() {
super(newSocketDomain(), false);
public EpollDomainSocketChannel(EventLoop eventLoop) {
super(eventLoop, newSocketDomain(), false);
}
EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
super(parent, new LinuxSocket(fd.intValue()));
EpollDomainSocketChannel(Channel parent, EventLoop eventLoop, FileDescriptor fd) {
super(parent, eventLoop, new LinuxSocket(fd.intValue()));
}
public EpollDomainSocketChannel(int fd) {
super(fd);
public EpollDomainSocketChannel(EventLoop eventLoop, int fd) {
super(eventLoop, fd);
}
public EpollDomainSocketChannel(Channel parent, LinuxSocket fd) {
super(parent, fd);
public EpollDomainSocketChannel(Channel parent, EventLoop eventLoop, LinuxSocket fd) {
super(parent, eventLoop, fd);
}
public EpollDomainSocketChannel(int fd, boolean active) {
super(new LinuxSocket(fd), active);
public EpollDomainSocketChannel(int fd, EventLoop eventLoop, boolean active) {
super(eventLoop, new LinuxSocket(fd), active);
}
@Override

View File

@ -16,6 +16,8 @@
package io.netty.channel.epoll;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.ServerDomainSocketChannel;
import io.netty.channel.unix.Socket;
@ -35,25 +37,26 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
private final EpollServerChannelConfig config = new EpollServerChannelConfig(this);
private volatile DomainSocketAddress local;
public EpollServerDomainSocketChannel() {
super(newSocketDomain(), false);
public EpollServerDomainSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
super(eventLoop, childEventLoopGroup, newSocketDomain(), false);
}
public EpollServerDomainSocketChannel(int fd) {
super(fd);
public EpollServerDomainSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, int fd) {
super(eventLoop, childEventLoopGroup, fd);
}
EpollServerDomainSocketChannel(LinuxSocket fd) {
super(fd);
EpollServerDomainSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, LinuxSocket fd) {
super(eventLoop, childEventLoopGroup, fd);
}
EpollServerDomainSocketChannel(LinuxSocket fd, boolean active) {
super(fd, active);
EpollServerDomainSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup,
LinuxSocket fd, boolean active) {
super(eventLoop, childEventLoopGroup, fd, active);
}
@Override
protected Channel newChildChannel(int fd, byte[] addr, int offset, int len) throws Exception {
return new EpollDomainSocketChannel(this, new Socket(fd));
return new EpollDomainSocketChannel(this, childEventLoopGroup().next(), new Socket(fd));
}
@Override

View File

@ -17,6 +17,7 @@ package io.netty.channel.epoll;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import java.io.IOException;
@ -39,24 +40,24 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
private final EpollServerSocketChannelConfig config;
private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
public EpollServerSocketChannel() {
super(newSocketStream(), false);
public EpollServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
super(eventLoop, childEventLoopGroup, newSocketStream(), false);
config = new EpollServerSocketChannelConfig(this);
}
public EpollServerSocketChannel(int fd) {
public EpollServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, int fd) {
// Must call this constructor to ensure this object's local address is configured correctly.
// The local address can only be obtained from a Socket object.
this(new LinuxSocket(fd));
this(eventLoop, childEventLoopGroup, new LinuxSocket(fd));
}
EpollServerSocketChannel(LinuxSocket fd) {
super(fd);
EpollServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, LinuxSocket fd) {
super(eventLoop, childEventLoopGroup, fd);
config = new EpollServerSocketChannelConfig(this);
}
EpollServerSocketChannel(LinuxSocket fd, boolean active) {
super(fd, active);
EpollServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, LinuxSocket fd, boolean active) {
super(eventLoop, childEventLoopGroup, fd, active);
config = new EpollServerSocketChannelConfig(this);
}
@ -92,7 +93,8 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
@Override
protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception {
return new EpollSocketChannel(this, new LinuxSocket(fd), address(address, offset, len));
return new EpollSocketChannel(this, childEventLoopGroup().next(), new LinuxSocket(fd),
address(address, offset, len));
}
Collection<InetAddress> tcpMd5SigAddresses() {

View File

@ -17,6 +17,7 @@ package io.netty.channel.epoll;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.concurrent.GlobalEventExecutor;
@ -41,23 +42,23 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
public EpollSocketChannel() {
super(newSocketStream(), false);
public EpollSocketChannel(EventLoop eventLoop) {
super(eventLoop, newSocketStream(), false);
config = new EpollSocketChannelConfig(this);
}
public EpollSocketChannel(int fd) {
super(fd);
public EpollSocketChannel(EventLoop eventLoop, int fd) {
super(eventLoop, fd);
config = new EpollSocketChannelConfig(this);
}
EpollSocketChannel(LinuxSocket fd, boolean active) {
super(fd, active);
EpollSocketChannel(EventLoop eventLoop, LinuxSocket fd, boolean active) {
super(eventLoop, fd, active);
config = new EpollSocketChannelConfig(this);
}
EpollSocketChannel(Channel parent, LinuxSocket fd, InetSocketAddress remoteAddress) {
super(parent, fd, remoteAddress);
EpollSocketChannel(Channel parent, EventLoop eventLoop, LinuxSocket fd, InetSocketAddress remoteAddress) {
super(parent, eventLoop, fd, remoteAddress);
config = new EpollSocketChannelConfig(this);
if (parent instanceof EpollServerSocketChannel) {

View File

@ -25,28 +25,38 @@ public class EpollChannelConfigTest {
@Test
public void testOptionGetThrowsChannelException() throws Exception {
Epoll.ensureAvailability();
EpollSocketChannel channel = new EpollSocketChannel();
channel.config().getSoLinger();
channel.fd().close();
EpollEventLoopGroup group = new EpollEventLoopGroup(1);
try {
EpollSocketChannel channel = new EpollSocketChannel(group.next());
channel.config().getSoLinger();
fail();
} catch (ChannelException e) {
// expected
channel.fd().close();
try {
channel.config().getSoLinger();
fail();
} catch (ChannelException e) {
// expected
}
} finally {
group.shutdownGracefully();
}
}
@Test
public void testOptionSetThrowsChannelException() throws Exception {
Epoll.ensureAvailability();
EpollSocketChannel channel = new EpollSocketChannel();
channel.config().setKeepAlive(true);
channel.fd().close();
EpollEventLoopGroup group = new EpollEventLoopGroup(1);
try {
EpollSocketChannel channel = new EpollSocketChannel(group.next());
channel.config().setKeepAlive(true);
fail();
} catch (ChannelException e) {
// expected
channel.fd().close();
try {
channel.config().setKeepAlive(true);
fail();
} catch (ChannelException e) {
// expected
}
} finally {
group.shutdownGracefully();
}
}
}

View File

@ -57,7 +57,7 @@ public class EpollDomainSocketFdTest extends AbstractSocketTest {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// Create new channel and obtain a file descriptor from it.
final EpollDomainSocketChannel ch = new EpollDomainSocketChannel();
final EpollDomainSocketChannel ch = new EpollDomainSocketChannel(ctx.channel().eventLoop());
ctx.writeAndFlush(ch.fd()).addListener(new ChannelFutureListener() {
@Override

View File

@ -77,7 +77,7 @@ public class EpollReuseAddrTest {
testMultipleBindDatagramChannelWithoutReusePortFails0(createBootstrap());
}
private static void testMultipleBindDatagramChannelWithoutReusePortFails0(AbstractBootstrap<?, ?> bootstrap) {
private static void testMultipleBindDatagramChannelWithoutReusePortFails0(AbstractBootstrap<?, ?, ?> bootstrap) {
bootstrap.handler(new DummyHandler());
ChannelFuture future = bootstrap.bind().syncUninterruptibly();
try {

View File

@ -16,6 +16,7 @@
package io.netty.channel.epoll;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.ConnectTimeoutException;
@ -51,10 +52,10 @@ public class EpollSocketTcpMd5Test {
@Before
public void setup() {
Bootstrap bootstrap = new Bootstrap();
ServerBootstrap bootstrap = new ServerBootstrap();
server = (EpollServerSocketChannel) bootstrap.group(GROUP)
.channel(EpollServerSocketChannel.class)
.handler(new ChannelInboundHandlerAdapter())
.childHandler(new ChannelInboundHandlerAdapter())
.bind(new InetSocketAddress(NetUtil.LOCALHOST4, 0)).syncUninterruptibly().channel();
}
@ -72,10 +73,10 @@ public class EpollSocketTcpMd5Test {
@Test
public void testServerOption() throws Exception {
Bootstrap bootstrap = new Bootstrap();
ServerBootstrap bootstrap = new ServerBootstrap();
EpollServerSocketChannel ch = (EpollServerSocketChannel) bootstrap.group(GROUP)
.channel(EpollServerSocketChannel.class)
.handler(new ChannelInboundHandlerAdapter())
.childHandler(new ChannelInboundHandlerAdapter())
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
ch.config().setOption(EpollChannelOption.TCP_MD5SIG,

View File

@ -19,6 +19,7 @@ import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.InternetProtocolFamily;
import io.netty.channel.socket.nio.NioDatagramChannel;
@ -128,8 +129,8 @@ class EpollSocketTestPermutation extends SocketTestPermutation {
public Bootstrap newInstance() {
return new Bootstrap().group(nioWorkerGroup).channelFactory(new ChannelFactory<Channel>() {
@Override
public Channel newChannel() {
return new NioDatagramChannel(InternetProtocolFamily.IPv4);
public Channel newChannel(EventLoop eventLoop) {
return new NioDatagramChannel(eventLoop, InternetProtocolFamily.IPv4);
}
@Override

View File

@ -73,8 +73,8 @@ abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChan
private volatile SocketAddress local;
private volatile SocketAddress remote;
AbstractKQueueChannel(Channel parent, BsdSocket fd, boolean active) {
super(parent);
AbstractKQueueChannel(Channel parent, EventLoop eventLoop, BsdSocket fd, boolean active) {
super(parent, eventLoop);
socket = checkNotNull(fd, "fd");
this.active = active;
if (active) {
@ -85,8 +85,8 @@ abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChan
}
}
AbstractKQueueChannel(Channel parent, BsdSocket fd, SocketAddress remote) {
super(parent);
AbstractKQueueChannel(Channel parent, EventLoop eventLoop, BsdSocket fd, SocketAddress remote) {
super(parent, eventLoop);
socket = checkNotNull(fd, "fd");
active = true;
// Directly cache the remote and local addresses

View File

@ -21,7 +21,9 @@ import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.UnstableApi;
import java.net.InetSocketAddress;
@ -30,13 +32,20 @@ import java.net.SocketAddress;
@UnstableApi
public abstract class AbstractKQueueServerChannel extends AbstractKQueueChannel implements ServerChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
private final EventLoopGroup childEventLoopGroup;
AbstractKQueueServerChannel(BsdSocket fd) {
this(fd, isSoErrorZero(fd));
AbstractKQueueServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, BsdSocket fd) {
this(eventLoop, childEventLoopGroup, fd, isSoErrorZero(fd));
}
AbstractKQueueServerChannel(BsdSocket fd, boolean active) {
super(null, fd, active);
AbstractKQueueServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, BsdSocket fd, boolean active) {
super(null, eventLoop, fd, active);
this.childEventLoopGroup = ObjectUtil.checkNotNull(childEventLoopGroup, "childEventLoopGroup");
}
@Override
public EventLoopGroup childEventLoopGroup() {
return childEventLoopGroup;
}
@Override

View File

@ -64,16 +64,16 @@ public abstract class AbstractKQueueStreamChannel extends AbstractKQueueChannel
}
};
AbstractKQueueStreamChannel(Channel parent, BsdSocket fd, boolean active) {
super(parent, fd, active);
AbstractKQueueStreamChannel(Channel parent, EventLoop eventLoop, BsdSocket fd, boolean active) {
super(parent, eventLoop, fd, active);
}
AbstractKQueueStreamChannel(Channel parent, BsdSocket fd, SocketAddress remote) {
super(parent, fd, remote);
AbstractKQueueStreamChannel(Channel parent, EventLoop eventLoop, BsdSocket fd, SocketAddress remote) {
super(parent, eventLoop, fd, remote);
}
AbstractKQueueStreamChannel(BsdSocket fd) {
this(null, fd, isSoErrorZero(fd));
AbstractKQueueStreamChannel(EventLoop eventLoop, BsdSocket fd) {
this(null, eventLoop, fd, isSoErrorZero(fd));
}
@Override

View File

@ -24,6 +24,7 @@ import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultAddressedEnvelope;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.DatagramChannelConfig;
import io.netty.channel.socket.DatagramPacket;
@ -40,8 +41,6 @@ import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import static io.netty.channel.kqueue.BsdSocket.newSocketDgram;
@ -58,17 +57,17 @@ public final class KQueueDatagramChannel extends AbstractKQueueChannel implement
private volatile boolean connected;
private final KQueueDatagramChannelConfig config;
public KQueueDatagramChannel() {
super(null, newSocketDgram(), false);
public KQueueDatagramChannel(EventLoop eventLoop) {
super(null, eventLoop, newSocketDgram(), false);
config = new KQueueDatagramChannelConfig(this);
}
public KQueueDatagramChannel(int fd) {
this(new BsdSocket(fd), true);
public KQueueDatagramChannel(EventLoop eventLoop, int fd) {
this(eventLoop, new BsdSocket(fd), true);
}
KQueueDatagramChannel(BsdSocket socket, boolean active) {
super(null, socket, active);
KQueueDatagramChannel(EventLoop eventLoop, BsdSocket socket, boolean active) {
super(null, eventLoop, socket, active);
config = new KQueueDatagramChannelConfig(this);
}

View File

@ -19,6 +19,7 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.DomainSocketChannel;
import io.netty.channel.unix.FileDescriptor;
@ -37,16 +38,16 @@ public final class KQueueDomainSocketChannel extends AbstractKQueueStreamChannel
private volatile DomainSocketAddress local;
private volatile DomainSocketAddress remote;
public KQueueDomainSocketChannel() {
super(null, newSocketDomain(), false);
public KQueueDomainSocketChannel(EventLoop eventLoop) {
super(null, eventLoop, newSocketDomain(), false);
}
public KQueueDomainSocketChannel(int fd) {
this(null, new BsdSocket(fd));
public KQueueDomainSocketChannel(EventLoop eventLoop, int fd) {
this(null, eventLoop, new BsdSocket(fd));
}
KQueueDomainSocketChannel(Channel parent, BsdSocket fd) {
super(parent, fd, true);
KQueueDomainSocketChannel(Channel parent, EventLoop eventLoop, BsdSocket fd) {
super(parent, eventLoop, fd, true);
}
@Override

View File

@ -16,6 +16,8 @@
package io.netty.channel.kqueue;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.unix.DomainSocketAddress;
import io.netty.channel.unix.ServerDomainSocketChannel;
import io.netty.util.internal.UnstableApi;
@ -36,21 +38,22 @@ public final class KQueueServerDomainSocketChannel extends AbstractKQueueServerC
private final KQueueServerChannelConfig config = new KQueueServerChannelConfig(this);
private volatile DomainSocketAddress local;
public KQueueServerDomainSocketChannel() {
super(newSocketDomain(), false);
public KQueueServerDomainSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
super(eventLoop, childEventLoopGroup, newSocketDomain(), false);
}
public KQueueServerDomainSocketChannel(int fd) {
this(new BsdSocket(fd), false);
public KQueueServerDomainSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, int fd) {
this(eventLoop, childEventLoopGroup, new BsdSocket(fd), false);
}
KQueueServerDomainSocketChannel(BsdSocket socket, boolean active) {
super(socket, active);
KQueueServerDomainSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup,
BsdSocket socket, boolean active) {
super(eventLoop, childEventLoopGroup, socket, active);
}
@Override
protected Channel newChildChannel(int fd, byte[] addr, int offset, int len) throws Exception {
return new KQueueDomainSocketChannel(this, new BsdSocket(fd));
return new KQueueDomainSocketChannel(this, childEventLoopGroup().next(), new BsdSocket(fd));
}
@Override

View File

@ -17,6 +17,7 @@ package io.netty.channel.kqueue;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.util.internal.UnstableApi;
@ -30,24 +31,24 @@ import static io.netty.channel.unix.NativeInetAddress.address;
public final class KQueueServerSocketChannel extends AbstractKQueueServerChannel implements ServerSocketChannel {
private final KQueueServerSocketChannelConfig config;
public KQueueServerSocketChannel() {
super(newSocketStream(), false);
public KQueueServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
super(eventLoop, childEventLoopGroup, newSocketStream(), false);
config = new KQueueServerSocketChannelConfig(this);
}
public KQueueServerSocketChannel(int fd) {
public KQueueServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, int fd) {
// Must call this constructor to ensure this object's local address is configured correctly.
// The local address can only be obtained from a Socket object.
this(new BsdSocket(fd));
this(eventLoop, childEventLoopGroup, new BsdSocket(fd));
}
KQueueServerSocketChannel(BsdSocket fd) {
super(fd);
KQueueServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, BsdSocket fd) {
super(eventLoop, childEventLoopGroup, fd);
config = new KQueueServerSocketChannelConfig(this);
}
KQueueServerSocketChannel(BsdSocket fd, boolean active) {
super(fd, active);
KQueueServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, BsdSocket fd, boolean active) {
super(eventLoop, childEventLoopGroup, fd, active);
config = new KQueueServerSocketChannelConfig(this);
}
@ -82,6 +83,7 @@ public final class KQueueServerSocketChannel extends AbstractKQueueServerChannel
@Override
protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception {
return new KQueueSocketChannel(this, new BsdSocket(fd), address(address, offset, len));
return new KQueueSocketChannel(this, childEventLoopGroup().next(),
new BsdSocket(fd), address(address, offset, len));
}
}

View File

@ -16,6 +16,7 @@
package io.netty.channel.kqueue;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.concurrent.GlobalEventExecutor;
@ -28,18 +29,18 @@ import java.util.concurrent.Executor;
public final class KQueueSocketChannel extends AbstractKQueueStreamChannel implements SocketChannel {
private final KQueueSocketChannelConfig config;
public KQueueSocketChannel() {
super(null, BsdSocket.newSocketStream(), false);
public KQueueSocketChannel(EventLoop eventLoop) {
super(null, eventLoop, BsdSocket.newSocketStream(), false);
config = new KQueueSocketChannelConfig(this);
}
public KQueueSocketChannel(int fd) {
super(new BsdSocket(fd));
public KQueueSocketChannel(EventLoop eventLoop, int fd) {
super(eventLoop, new BsdSocket(fd));
config = new KQueueSocketChannelConfig(this);
}
KQueueSocketChannel(Channel parent, BsdSocket fd, InetSocketAddress remoteAddress) {
super(parent, fd, remoteAddress);
KQueueSocketChannel(Channel parent, EventLoop eventLoop, BsdSocket fd, InetSocketAddress remoteAddress) {
super(parent, eventLoop, fd, remoteAddress);
config = new KQueueSocketChannelConfig(this);
}

View File

@ -35,27 +35,37 @@ public class KQueueChannelConfigTest {
@Test
public void testOptionGetThrowsChannelException() throws Exception {
KQueueSocketChannel channel = new KQueueSocketChannel();
channel.config().getSoLinger();
channel.fd().close();
KQueueEventLoopGroup group = new KQueueEventLoopGroup(1);
try {
KQueueSocketChannel channel = new KQueueSocketChannel(group.next());
channel.config().getSoLinger();
fail();
} catch (ChannelException e) {
// expected
channel.fd().close();
try {
channel.config().getSoLinger();
fail();
} catch (ChannelException e) {
// expected
}
} finally {
group.shutdownGracefully();
}
}
@Test
public void testOptionSetThrowsChannelException() throws Exception {
KQueueSocketChannel channel = new KQueueSocketChannel();
channel.config().setKeepAlive(true);
channel.fd().close();
KQueueEventLoopGroup group = new KQueueEventLoopGroup(1);
try {
KQueueSocketChannel channel = new KQueueSocketChannel(group.next());
channel.config().setKeepAlive(true);
fail();
} catch (ChannelException e) {
// expected
channel.fd().close();
try {
channel.config().setKeepAlive(true);
fail();
} catch (ChannelException e) {
// expected
}
} finally {
group.shutdownGracefully();
}
}

View File

@ -56,7 +56,7 @@ public class KQueueDomainSocketFdTest extends AbstractSocketTest {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// Create new channel and obtain a file descriptor from it.
final KQueueDomainSocketChannel ch = new KQueueDomainSocketChannel();
final KQueueDomainSocketChannel ch = new KQueueDomainSocketChannel(ctx.channel().eventLoop());
ctx.writeAndFlush(ch.fd()).addListener(new ChannelFutureListener() {
@Override

View File

@ -19,6 +19,7 @@ import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.InternetProtocolFamily;
import io.netty.channel.socket.nio.NioDatagramChannel;
@ -112,8 +113,8 @@ class KQueueSocketTestPermutation extends SocketTestPermutation {
public Bootstrap newInstance() {
return new Bootstrap().group(nioWorkerGroup).channelFactory(new ChannelFactory<Channel>() {
@Override
public Channel newChannel() {
return new NioDatagramChannel(InternetProtocolFamily.IPv4);
public Channel newChannel(EventLoop eventLoop) {
return new NioDatagramChannel(eventLoop, InternetProtocolFamily.IPv4);
}
@Override

View File

@ -27,6 +27,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.nio.AbstractNioMessageChannel;
import io.netty.channel.sctp.DefaultSctpChannelConfig;
@ -79,15 +80,15 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett
/**
* Create a new instance
*/
public NioSctpChannel() {
this(newSctpChannel());
public NioSctpChannel(EventLoop eventLoop) {
this(eventLoop, newSctpChannel());
}
/**
* Create a new instance using {@link SctpChannel}
*/
public NioSctpChannel(SctpChannel sctpChannel) {
this(null, sctpChannel);
public NioSctpChannel(EventLoop eventLoop, SctpChannel sctpChannel) {
this(null, eventLoop, sctpChannel);
}
/**
@ -97,8 +98,8 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett
* or {@code null}.
* @param sctpChannel the underlying {@link SctpChannel}
*/
public NioSctpChannel(Channel parent, SctpChannel sctpChannel) {
super(parent, sctpChannel, SelectionKey.OP_READ);
public NioSctpChannel(Channel parent, EventLoop eventLoop, SctpChannel sctpChannel) {
super(parent, eventLoop, sctpChannel, SelectionKey.OP_READ);
try {
sctpChannel.configureBlocking(false);
config = new NioSctpChannelConfig(this, sctpChannel);

View File

@ -22,9 +22,12 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.AbstractNioMessageChannel;
import io.netty.channel.sctp.DefaultSctpServerChannelConfig;
import io.netty.channel.sctp.SctpServerChannelConfig;
import io.netty.util.internal.ObjectUtil;
import java.io.IOException;
import java.net.InetAddress;
@ -57,14 +60,21 @@ public class NioSctpServerChannel extends AbstractNioMessageChannel
}
}
private final EventLoopGroup childEventLoopGroup;
private final SctpServerChannelConfig config;
/**
* Create a new instance
*/
public NioSctpServerChannel() {
super(null, newSocket(), SelectionKey.OP_ACCEPT);
public NioSctpServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
super(null, eventLoop, newSocket(), SelectionKey.OP_ACCEPT);
config = new NioSctpServerChannelConfig(this, javaChannel());
this.childEventLoopGroup = ObjectUtil.checkNotNull(childEventLoopGroup, "childEventLoopGroup");
}
@Override
public EventLoopGroup childEventLoopGroup() {
return childEventLoopGroup;
}
@Override
@ -140,7 +150,7 @@ public class NioSctpServerChannel extends AbstractNioMessageChannel
if (ch == null) {
return 0;
}
buf.add(new NioSctpChannel(this, ch));
buf.add(new NioSctpChannel(this, childEventLoopGroup().next(), ch));
return 1;
}

View File

@ -22,14 +22,10 @@ import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ReflectiveChannelFactory;
import io.netty.util.internal.SocketUtils;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
@ -47,11 +43,10 @@ import java.util.Map;
* <p>When not used in a {@link ServerBootstrap} context, the {@link #bind()} methods are useful for connectionless
* transports such as datagram (UDP).</p>
*/
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C, F>, C extends Channel, F>
implements Cloneable {
volatile EventLoopGroup group;
@SuppressWarnings("deprecation")
private volatile ChannelFactory<? extends C> channelFactory;
private volatile SocketAddress localAddress;
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
@ -61,9 +56,8 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
// Disallow extending from a different package.
}
AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {
AbstractBootstrap(AbstractBootstrap<B, C, F> bootstrap) {
group = bootstrap.group;
channelFactory = bootstrap.channelFactory;
handler = bootstrap.handler;
localAddress = bootstrap.localAddress;
synchronized (bootstrap.options) {
@ -94,46 +88,6 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
return (B) this;
}
/**
* The {@link Class} which is used to create {@link Channel} instances from.
* You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
* {@link Channel} implementation has no no-args constructor.
*/
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
/**
* @deprecated Use {@link #channelFactory(io.netty.channel.ChannelFactory)} instead.
*/
@Deprecated
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return self();
}
/**
* {@link io.netty.channel.ChannelFactory} which is used to create {@link Channel} instances from
* when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
* is not working for you because of some more complex needs. If your {@link Channel} implementation
* has a no-args constructor, its highly recommend to just use {@link #channel(Class)} to
* simplify your code.
*/
@SuppressWarnings({ "unchecked", "deprecation" })
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
/**
* The {@link SocketAddress} which is used to bind the local "end" to.
*/
@ -211,9 +165,6 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
if (group == null) {
throw new IllegalStateException("group not set");
}
if (channelFactory == null) {
throw new IllegalStateException("channel or channelFactory not set");
}
return self();
}
@ -292,7 +243,7 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
final ChannelPromise promise = channel.newPromise();
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
@ -302,10 +253,6 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
@ -315,43 +262,38 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
}
final ChannelFuture initAndRegister() {
Channel channel = null;
EventLoop loop = group.next();
final Channel channel;
try {
channel = channelFactory.newChannel();
init(channel);
channel = newChannel(loop);
} catch (Throwable t) {
if (channel != null) {
// channel can be null if newChannel crashed (eg SocketException("too many open files"))
channel.unsafe().closeForcibly();
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
// as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
return new FailedChannel(loop).newFailedFuture(t);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
final ChannelPromise promise = channel.newPromise();
loop.execute(new Runnable() {
@Override
public void run() {
init(channel).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
channel.register(promise);
} else {
channel.unsafe().closeForcibly();
promise.setFailure(future.cause());
}
}
});
}
}
});
// If we are here and the promise is not failed, it's one of the following cases:
// 1) If we attempted registration from the event loop, the registration has been completed at this point.
// i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
// 2) If we attempted registration from the other thread, the registration request has been successfully
// added to the event loop's task queue for later execution.
// i.e. It's safe to attempt bind() or connect() now:
// because bind() or connect() will be executed *after* the scheduled registration task is executed
// because register(), bind(), and connect() are all bound to the same thread.
return regFuture;
return promise;
}
abstract void init(Channel channel) throws Exception;
abstract C newChannel(EventLoop loop) throws Exception;
abstract ChannelFuture init(Channel channel);
private static void doBind0(
final ChannelFuture regFuture, final Channel channel,
@ -396,7 +338,7 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
* Returns the {@link AbstractBootstrapConfig} object that can be used to obtain the current config
* of the bootstrap.
*/
public abstract AbstractBootstrapConfig<B, C> config();
public abstract AbstractBootstrapConfig<B, C, F> config();
static <K, V> Map<K, V> copiedMap(Map<K, V> map) {
final Map<K, V> copied;
@ -421,11 +363,6 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
return localAddress;
}
@SuppressWarnings("deprecation")
final ChannelFactory<? extends C> channelFactory() {
return channelFactory;
}
final ChannelHandler handler() {
return handler;
}
@ -472,31 +409,4 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C ext
.append('(').append(config()).append(')');
return buf.toString();
}
static final class PendingRegistrationPromise extends DefaultChannelPromise {
// Is set to the correct EventExecutor once the registration was successful. Otherwise it will
// stay null and so the GlobalEventExecutor.INSTANCE will be used for notifications.
private volatile boolean registered;
PendingRegistrationPromise(Channel channel) {
super(channel);
}
void registered() {
registered = true;
}
@Override
protected EventExecutor executor() {
if (registered) {
// If the registration was a success executor is set.
//
// See https://github.com/netty/netty/issues/2586
return super.executor();
}
// The registration failed so we can only use the GlobalEventExecutor as last resort to notify.
return GlobalEventExecutor.INSTANCE;
}
}
}

View File

@ -16,9 +16,11 @@
package io.netty.bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannelFactory;
import io.netty.util.AttributeKey;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.StringUtil;
@ -29,11 +31,11 @@ import java.util.Map;
/**
* Exposes the configuration of an {@link AbstractBootstrap}.
*/
public abstract class AbstractBootstrapConfig<B extends AbstractBootstrap<B, C>, C extends Channel> {
public abstract class AbstractBootstrapConfig<B extends AbstractBootstrap<B, C, F>, C extends Channel, F> {
protected final B bootstrap;
protected AbstractBootstrapConfig(B bootstrap) {
AbstractBootstrapConfig(B bootstrap) {
this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap");
}
@ -45,12 +47,11 @@ public abstract class AbstractBootstrapConfig<B extends AbstractBootstrap<B, C>,
}
/**
* Returns the configured {@link ChannelFactory} or {@code null} if non is configured yet.
* Returns the configured {@link ChannelFactory} / {@link ServerChannelFactory} or {@code null}
* if non is configured yet.
*/
@SuppressWarnings("deprecation")
public final ChannelFactory<? extends C> channelFactory() {
return bootstrap.channelFactory();
}
public abstract F channelFactory();
/**
* Returns the configured {@link ChannelHandler} or {@code null} if non is configured yet.
@ -93,7 +94,7 @@ public abstract class AbstractBootstrapConfig<B extends AbstractBootstrap<B, C>,
.append(", ");
}
@SuppressWarnings("deprecation")
ChannelFactory<? extends C> factory = channelFactory();
F factory = channelFactory();
if (factory != null) {
buf.append("channelFactory: ")
.append(factory)

View File

@ -16,6 +16,7 @@
package io.netty.bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
@ -23,6 +24,7 @@ import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ReflectiveChannelFactory;
import io.netty.resolver.AddressResolver;
import io.netty.resolver.DefaultAddressResolverGroup;
import io.netty.resolver.NameResolver;
@ -46,7 +48,7 @@ import java.util.Map.Entry;
* <p>The {@link #bind()} methods are useful in combination with connectionless transports such as datagram (UDP).
* For regular TCP connections, please use the provided {@link #connect()} methods.</p>
*/
public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel, ChannelFactory<? extends Channel>> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Bootstrap.class);
@ -58,6 +60,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
private volatile AddressResolverGroup<SocketAddress> resolver =
(AddressResolverGroup<SocketAddress>) DEFAULT_RESOLVER;
private volatile SocketAddress remoteAddress;
volatile ChannelFactory<? extends Channel> channelFactory;
public Bootstrap() { }
@ -65,6 +68,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
super(bootstrap);
resolver = bootstrap.resolver;
remoteAddress = bootstrap.remoteAddress;
channelFactory = bootstrap.channelFactory;
}
/**
@ -73,7 +77,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
* @param resolver the {@link NameResolver} for this {@code Bootstrap}; may be {@code null}, in which case a default
* resolver will be used
*
* @see io.netty.resolver.DefaultAddressResolverGroup
* @see DefaultAddressResolverGroup
*/
@SuppressWarnings("unchecked")
public Bootstrap resolver(AddressResolverGroup<?> resolver) {
@ -106,6 +110,37 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
return this;
}
/**
* The {@link Class} which is used to create {@link Channel} instances from.
* You either use this or {@link #channelFactory(ChannelFactory)} if your
* {@link Channel} implementation has no no-args constructor.
*/
public Bootstrap channel(Class<? extends Channel> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<Channel>(channelClass));
}
/**
* {@link ChannelFactory} which is used to create {@link Channel} instances from
* when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
* is not working for you because of some more complex needs. If your {@link Channel} implementation
* has a no-args constructor, its highly recommend to just use {@link #channel(Class)} to
* simplify your code.
*/
public Bootstrap channelFactory(ChannelFactory<? extends Channel> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return this;
}
/**
* Connect a {@link Channel} to the remote peer.
*/
@ -170,7 +205,7 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
final ChannelPromise promise = channel.newPromise();
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
@ -182,9 +217,6 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
}
}
@ -260,7 +292,8 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
@Override
@SuppressWarnings("unchecked")
void init(Channel channel) throws Exception {
ChannelFuture init(Channel channel) {
ChannelPromise promise = channel.newPromise();
ChannelPipeline p = channel.pipeline();
p.addLast(config.handler());
@ -275,6 +308,12 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
}
return promise.setSuccess();
}
@Override
Channel newChannel(EventLoop eventLoop) throws Exception {
return channelFactory.newChannel(eventLoop);
}
@Override
@ -283,6 +322,9 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
if (config.handler() == null) {
throw new IllegalStateException("handler not set");
}
if (config.channelFactory() == null) {
throw new IllegalStateException("channelFactory not set");
}
return this;
}

View File

@ -16,6 +16,7 @@
package io.netty.bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFactory;
import io.netty.resolver.AddressResolverGroup;
import java.net.SocketAddress;
@ -23,12 +24,18 @@ import java.net.SocketAddress;
/**
* Exposes the configuration of a {@link Bootstrap}.
*/
public final class BootstrapConfig extends AbstractBootstrapConfig<Bootstrap, Channel> {
public final class BootstrapConfig extends
AbstractBootstrapConfig<Bootstrap, Channel, ChannelFactory<? extends Channel>> {
BootstrapConfig(Bootstrap bootstrap) {
super(bootstrap);
}
@Override
public ChannelFactory<? extends Channel> channelFactory() {
return bootstrap.channelFactory;
}
/**
* Returns the configured remote address or {@code null} if non is configured yet.
*/

View File

@ -29,8 +29,8 @@ final class FailedChannel extends AbstractChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
private final ChannelConfig config = new DefaultChannelConfig(this);
FailedChannel() {
super(null);
FailedChannel(EventLoop eventLoop) {
super(null, eventLoop);
}
@Override
@ -40,7 +40,7 @@ final class FailedChannel extends AbstractChannel {
@Override
protected boolean isCompatible(EventLoop loop) {
return false;
return true;
}
@Override

View File

@ -25,8 +25,12 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ReflectiveServerChannelFactory;
import io.netty.channel.ServerChannel;
import io.netty.channel.ServerChannelFactory;
import io.netty.util.AttributeKey;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -40,7 +44,8 @@ import java.util.concurrent.TimeUnit;
* {@link Bootstrap} sub-class which allows easy bootstrap of {@link ServerChannel}
*
*/
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel,
ServerChannelFactory<? extends ServerChannel>> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);
@ -49,6 +54,7 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler;
volatile ServerChannelFactory<? extends ServerChannel> channelFactory;
public ServerBootstrap() { }
@ -56,6 +62,7 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
super(bootstrap);
childGroup = bootstrap.childGroup;
childHandler = bootstrap.childHandler;
channelFactory = bootstrap.channelFactory;
synchronized (bootstrap.childOptions) {
childOptions.putAll(bootstrap.childOptions);
}
@ -137,8 +144,41 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
return this;
}
/**
* The {@link Class} which is used to create {@link ServerChannel} instances from.
* You either use this or {@link #channelFactory(ServerChannelFactory)} if your
* {@link Channel} implementation has no no-args constructor.
*/
public ServerBootstrap channel(Class<? extends ServerChannel> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveServerChannelFactory<ServerChannel>(channelClass));
}
/**
* {@link ServerChannelFactory} which is used to create {@link Channel} instances from
* when calling {@link #bind()}. This method is usually only used if {@link #channel(Class)}
* is not working for you because of some more complex needs. If your {@link Channel} implementation
* has a no-args constructor, its highly recommend to just use {@link #channel(Class)} to
* simplify your code.
*/
public ServerBootstrap channelFactory(ServerChannelFactory<? extends ServerChannel> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return this;
}
@Override
void init(Channel channel) throws Exception {
ChannelFuture init(Channel channel) {
final ChannelPromise promise = channel.newPromise();
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
@ -155,7 +195,6 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
ChannelPipeline p = channel.pipeline();
final EventLoopGroup currentChildGroup = childGroup;
final ChannelHandler currentChildHandler = childHandler;
final Entry<ChannelOption<?>, Object>[] currentChildOptions;
final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
@ -179,11 +218,18 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
ch, currentChildHandler, currentChildOptions, currentChildAttrs));
promise.setSuccess();
}
});
}
});
return promise;
}
@Override
ServerChannel newChannel(EventLoop eventLoop) throws Exception {
return channelFactory.newChannel(eventLoop, childGroup);
}
@Override
@ -192,6 +238,9 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
if (childHandler == null) {
throw new IllegalStateException("childHandler not set");
}
if (channelFactory == null) {
throw new IllegalStateException("channelFactory not set");
}
if (childGroup == null) {
logger.warn("childGroup is not set. Using parentGroup instead.");
childGroup = config.group();
@ -211,16 +260,14 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter {
private final EventLoopGroup childGroup;
private final ChannelHandler childHandler;
private final Entry<ChannelOption<?>, Object>[] childOptions;
private final Entry<AttributeKey<?>, Object>[] childAttrs;
private final Runnable enableAutoReadTask;
ServerBootstrapAcceptor(
final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,
final Channel channel, ChannelHandler childHandler,
Entry<ChannelOption<?>, Object>[] childOptions, Entry<AttributeKey<?>, Object>[] childAttrs) {
this.childGroup = childGroup;
this.childHandler = childHandler;
this.childOptions = childOptions;
this.childAttrs = childAttrs;
@ -239,20 +286,40 @@ public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerCh
}
@Override
@SuppressWarnings("unchecked")
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
EventLoop childEventLoop = child.eventLoop();
// Ensure we always execute on the child EventLoop.
if (childEventLoop.inEventLoop()) {
initChild(child);
} else {
try {
childEventLoop.execute(new Runnable() {
@Override
public void run() {
initChild(child);
}
});
} catch (Throwable cause) {
forceClose(child, cause);
}
}
}
@SuppressWarnings("unchecked")
private void initChild(final Channel child) {
assert child.eventLoop().inEventLoop();
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
for (Entry<AttributeKey<?>, Object> e : childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}
child.register().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {

View File

@ -19,6 +19,7 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.ServerChannelFactory;
import io.netty.util.AttributeKey;
import io.netty.util.internal.StringUtil;
@ -27,7 +28,8 @@ import java.util.Map;
/**
* Exposes the configuration of a {@link ServerBootstrapConfig}.
*/
public final class ServerBootstrapConfig extends AbstractBootstrapConfig<ServerBootstrap, ServerChannel> {
public final class ServerBootstrapConfig extends AbstractBootstrapConfig<ServerBootstrap, ServerChannel,
ServerChannelFactory<? extends ServerChannel>> {
ServerBootstrapConfig(ServerBootstrap bootstrap) {
super(bootstrap);
@ -64,6 +66,11 @@ public final class ServerBootstrapConfig extends AbstractBootstrapConfig<ServerB
return bootstrap.childAttrs();
}
@Override
public ServerChannelFactory<? extends ServerChannel> channelFactory() {
return bootstrap.channelFactory;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(super.toString());

View File

@ -20,6 +20,7 @@ import io.netty.channel.socket.ChannelOutputShutdownEvent;
import io.netty.channel.socket.ChannelOutputShutdownException;
import io.netty.util.DefaultAttributeMap;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.ThrowableUtil;
import io.netty.util.internal.UnstableApi;
@ -58,13 +59,13 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
private final Channel parent;
private final ChannelId id;
private final Unsafe unsafe;
private final DefaultChannelPipeline pipeline;
private final ChannelPipeline pipeline;
private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false);
private final CloseFuture closeFuture = new CloseFuture(this);
private volatile SocketAddress localAddress;
private volatile SocketAddress remoteAddress;
private volatile EventLoop eventLoop;
private final EventLoop eventLoop;
private volatile boolean registered;
private boolean closeInitiated;
@ -78,8 +79,9 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
* @param parent
* the parent of this channel. {@code null} if there's no parent.
*/
protected AbstractChannel(Channel parent) {
protected AbstractChannel(Channel parent, EventLoop eventLoop) {
this.parent = parent;
this.eventLoop = validateEventLoop(eventLoop);
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
@ -91,13 +93,21 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
* @param parent
* the parent of this channel. {@code null} if there's no parent.
*/
protected AbstractChannel(Channel parent, ChannelId id) {
protected AbstractChannel(Channel parent, EventLoop eventLoop, ChannelId id) {
this.parent = parent;
this.eventLoop = validateEventLoop(eventLoop);
this.id = id;
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
private EventLoop validateEventLoop(EventLoop eventLoop) {
if (!isCompatible(ObjectUtil.checkNotNull(eventLoop, "eventLoop"))) {
throw new IllegalArgumentException("incompatible event loop type: " + eventLoop.getClass().getName());
}
return eventLoop;
}
@Override
public final ChannelId id() {
return id;
@ -105,16 +115,17 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
/**
* Returns a new {@link DefaultChannelId} instance. Subclasses may override this method to assign custom
* {@link ChannelId}s to {@link Channel}s that use the {@link AbstractChannel#AbstractChannel(Channel)} constructor.
* {@link ChannelId}s to {@link Channel}s that use the {@link AbstractChannel#AbstractChannel(Channel, EventLoop)}
* constructor.
*/
protected ChannelId newId() {
return DefaultChannelId.newInstance();
}
/**
* Returns a new {@link DefaultChannelPipeline} instance.
* Returns a new {@link ChannelPipeline} instance.
*/
protected DefaultChannelPipeline newChannelPipeline() {
protected ChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
@ -157,10 +168,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
@Override
public EventLoop eventLoop() {
EventLoop eventLoop = this.eventLoop;
if (eventLoop == null) {
throw new IllegalStateException("channel not registered to an event loop");
}
return eventLoop;
}
@ -242,6 +249,11 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
return pipeline.close();
}
@Override
public ChannelFuture register() {
return pipeline.register();
}
@Override
public ChannelFuture deregister() {
return pipeline.deregister();
@ -278,6 +290,11 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
return pipeline.close(promise);
}
@Override
public ChannelFuture register(ChannelPromise promise) {
return pipeline.register(promise);
}
@Override
public ChannelFuture deregister(ChannelPromise promise) {
return pipeline.deregister(promise);
@ -434,12 +451,14 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
private volatile ChannelOutboundBuffer outboundBuffer = new ChannelOutboundBuffer(AbstractChannel.this);
private RecvByteBufAllocator.Handle recvHandle;
private MessageSizeEstimator.Handle estimatorHandler;
private boolean inFlush0;
/** true if the channel has never been registered, false otherwise */
private boolean neverRegistered = true;
private void assertEventLoop() {
assert !registered || eventLoop.inEventLoop();
assert eventLoop.inEventLoop();
}
@Override
@ -466,44 +485,14 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
}
@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
if (eventLoop == null) {
throw new NullPointerException("eventLoop");
}
public final void register(final ChannelPromise promise) {
assertEventLoop();
if (isRegistered()) {
promise.setFailure(new IllegalStateException("registered to an event loop already"));
return;
}
if (!isCompatible(eventLoop)) {
promise.setFailure(
new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName()));
return;
}
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
} catch (Throwable t) {
logger.warn(
"Force-closing a channel whose registration task was not accepted by an event loop: {}",
AbstractChannel.this, t);
closeForcibly();
closeFuture.setClosed();
safeSetFailure(promise, t);
}
}
}
private void register0(ChannelPromise promise) {
try {
// check if the channel is still open as it could be closed in the mean time when the register
// call was outside of the eventLoop
@ -515,10 +504,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
neverRegistered = false;
registered = true;
// Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
// user may already fire events through the pipeline in the ChannelFutureListener.
pipeline.invokeHandlerAddedIfNeeded();
safeSetSuccess(promise);
pipeline.fireChannelRegistered();
// Only fire a channelActive if the channel has never been registered. This prevents firing
@ -781,8 +766,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
@Override
public final void closeForcibly() {
assertEventLoop();
try {
doClose();
} catch (Exception e) {
@ -881,7 +864,10 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
int size;
try {
msg = filterOutboundMessage(msg);
size = pipeline.estimatorHandle().size(msg);
if (estimatorHandler == null) {
estimatorHandler = config().getMessageSizeEstimator().newHandle();
}
size = estimatorHandler.size(msg);
if (size < 0) {
size = 0;
}

View File

@ -465,6 +465,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
return close(newPromise());
}
@Override
public ChannelFuture register() {
return register(newPromise());
}
@Override
public ChannelFuture deregister() {
return deregister(newPromise());
@ -630,6 +635,41 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
}
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
if (isNotValidPromise(promise, false)) {
// cancelled
return promise;
}
final AbstractChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeRegister(promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeRegister(promise);
}
}, promise, null);
}
return promise;
}
private void invokeRegister(ChannelPromise promise) {
if (invokeHandler()) {
try {
((ChannelOutboundHandler) handler()).register(this, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
} else {
register(promise);
}
}
@Override
public ChannelFuture deregister(final ChannelPromise promise) {
if (isNotValidPromise(promise, false)) {

View File

@ -15,6 +15,8 @@
*/
package io.netty.channel;
import io.netty.util.internal.ObjectUtil;
import java.net.SocketAddress;
/**
@ -31,11 +33,19 @@ import java.net.SocketAddress;
public abstract class AbstractServerChannel extends AbstractChannel implements ServerChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
private final EventLoopGroup childEventLoopGroup;
/**
* Creates a new instance.
*/
protected AbstractServerChannel() {
super(null);
protected AbstractServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
super(null, eventLoop);
this.childEventLoopGroup = ObjectUtil.checkNotNull(childEventLoopGroup, "childEventLoopGroup");
}
@Override
public EventLoopGroup childEventLoopGroup() {
return childEventLoopGroup;
}
@Override

View File

@ -230,7 +230,7 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparabl
* Register the {@link Channel} of the {@link ChannelPromise} and notify
* the {@link ChannelFuture} once the registration was complete.
*/
void register(EventLoop eventLoop, ChannelPromise promise);
void register(ChannelPromise promise);
/**
* Bind the {@link SocketAddress} to the {@link Channel} of the {@link ChannelPromise} and notify

View File

@ -73,6 +73,17 @@ public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implement
ctx.close(promise);
}
/**
* Calls {@link ChannelHandlerContext#register(ChannelPromise)} to forward
* to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}.
*
* Sub-classes may override this method to change behavior.
*/
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.register(promise);
}
/**
* Calls {@link ChannelHandlerContext#deregister(ChannelPromise)} to forward
* to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}.

View File

@ -18,11 +18,9 @@ package io.netty.channel;
/**
* Creates a new {@link Channel}.
*/
@SuppressWarnings({ "ClassNameSameAsAncestorName", "deprecation" })
public interface ChannelFactory<T extends Channel> extends io.netty.bootstrap.ChannelFactory<T> {
public interface ChannelFactory<T extends Channel> {
/**
* Creates a new channel.
*/
@Override
T newChannel();
T newChannel(EventLoop eventLoop) throws Exception;
}

View File

@ -21,10 +21,6 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was
* registered to its {@link EventLoop}.
@ -54,10 +50,6 @@ import java.util.concurrent.ConcurrentHashMap;
public abstract class ChannelInitializer<C extends Channel> extends ChannelInboundHandlerAdapter {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class);
// We use a Set as a ChannelInitializer is usually shared between all Channels in a Bootstrap /
// ServerBootstrap. This way we can reduce the memory usage compared to use Attributes.
private final Set<ChannelHandlerContext> initMap = Collections.newSetFromMap(
new ConcurrentHashMap<ChannelHandlerContext, Boolean>());
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
@ -70,24 +62,6 @@ public abstract class ChannelInitializer<C extends Channel> extends ChannelInbou
*/
protected abstract void initChannel(C ch) throws Exception;
@Override
@SuppressWarnings("unchecked")
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
// Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
// the handler.
if (initChannel(ctx)) {
// we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
// miss an event.
ctx.pipeline().fireChannelRegistered();
// We are done with init the Channel, removing all the state for the Channel now.
removeState(ctx);
} else {
// Called initChannel(...) before which is the expected behavior, so just forward the event.
ctx.fireChannelRegistered();
}
}
/**
* Handle the {@link Throwable} by logging and closing the {@link Channel}. Sub-classes may override this.
*/
@ -102,59 +76,20 @@ public abstract class ChannelInitializer<C extends Channel> extends ChannelInbou
/**
* {@inheritDoc} If override this method ensure you call super!
*/
@SuppressWarnings("unchecked")
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isRegistered()) {
// This should always be true with our current DefaultChannelPipeline implementation.
// The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
// surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
// will be added in the expected order.
if (initChannel(ctx)) {
// We are done with init the Channel, removing the initializer now.
removeState(ctx);
try {
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(ctx, cause);
} finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
initMap.remove(ctx);
}
@SuppressWarnings("unchecked")
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
if (initMap.add(ctx)) { // Guard against re-entrance.
try {
initChannel((C) ctx.channel());
} catch (Throwable cause) {
// Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
// We do so to prevent multiple calls to initChannel(...).
exceptionCaught(ctx, cause);
} finally {
ChannelPipeline pipeline = ctx.pipeline();
if (pipeline.context(this) != null) {
pipeline.remove(this);
}
}
return true;
}
return false;
}
private void removeState(final ChannelHandlerContext ctx) {
// The removal may happen in an async fashion if the EventExecutor we use does something funky.
if (ctx.isRemoved()) {
initMap.remove(ctx);
} else {
// The context is not removed yet which is most likely the case because a custom EventExecutor is used.
// Let's schedule it on the EventExecutor to give it some more time to be completed in case it is offloaded.
ctx.executor().execute(new Runnable() {
@Override
public void run() {
initMap.remove(ctx);
}
});
}
}
}

View File

@ -62,10 +62,19 @@ public interface ChannelOutboundHandler extends ChannelHandler {
*/
void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
/**
* Called once a register operation is made to register for IO on the {@link EventLoop}.
*
* @param ctx the {@link ChannelHandlerContext} for which the register operation is made
* @param promise the {@link ChannelPromise} to notify once the operation completes
* @throws Exception thrown if an error occurs
*/
void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
/**
* Called once a deregister operation is made from the current registered {@link EventLoop}.
*
* @param ctx the {@link ChannelHandlerContext} for which the close operation is made
* @param ctx the {@link ChannelHandlerContext} for which the deregister operation is made
* @param promise the {@link ChannelPromise} to notify once the operation completes
* @throws Exception thrown if an error occurs
*/

View File

@ -71,6 +71,17 @@ public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter impleme
ctx.close(promise);
}
/**
* Calls {@link ChannelHandlerContext#register(ChannelPromise)} to forward
* to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}.
*
* Sub-classes may override this method to change behavior.
*/
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
ctx.register(promise);
}
/**
* Calls {@link ChannelHandlerContext#deregister(ChannelPromise)} to forward
* to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}.

View File

@ -86,6 +86,19 @@ public interface ChannelOutboundInvoker {
*/
ChannelFuture close();
/**
* Request to register on the {@link EventExecutor} for I/O processing.
* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of
* an error.
* <p>
* This will result in having the
* {@link ChannelOutboundHandler#register(ChannelHandlerContext, ChannelPromise)}
* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*
*/
ChannelFuture register();
/**
* Request to deregister from the previous assigned {@link EventExecutor} and notify the
* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of
@ -172,6 +185,20 @@ public interface ChannelOutboundInvoker {
*/
ChannelFuture close(ChannelPromise promise);
/**
* Request to register on the {@link EventExecutor} for I/O processing.
* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of
* an error.
*
* The given {@link ChannelPromise} will be notified.
* <p>
* This will result in having the
* {@link ChannelOutboundHandler#register(ChannelHandlerContext, ChannelPromise)}
* method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
ChannelFuture register(ChannelPromise promise);
/**
* Request to deregister from the previous assigned {@link EventExecutor} and notify the
* {@link ChannelFuture} once the operation completes, either because the operation was successful or because of

View File

@ -321,6 +321,16 @@ public class CombinedChannelDuplexHandler<I extends ChannelInboundHandler, O ext
}
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.register(outboundCtx, promise);
} else {
outboundCtx.register(promise);
}
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
assert ctx == outboundCtx.ctx;
@ -476,6 +486,11 @@ public class CombinedChannelDuplexHandler<I extends ChannelInboundHandler, O ext
return ctx.close();
}
@Override
public ChannelFuture register() {
return ctx.register();
}
@Override
public ChannelFuture deregister() {
return ctx.deregister();
@ -507,6 +522,11 @@ public class CombinedChannelDuplexHandler<I extends ChannelInboundHandler, O ext
return ctx.close(promise);
}
@Override
public ChannelFuture register(ChannelPromise promise) {
return ctx.register(promise);
}
@Override
public ChannelFuture deregister(ChannelPromise promise) {
return ctx.deregister(promise);

View File

@ -71,23 +71,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private Map<EventExecutorGroup, EventExecutor> childExecutors;
private volatile MessageSizeEstimator.Handle estimatorHandle;
private boolean firstRegistration = true;
/**
* This is the head of a linked list that is processed by {@link #callHandlerAddedForAllHandlers()} and so process
* all the pending {@link #callHandlerAdded0(AbstractChannelHandlerContext)}.
*
* We only keep the head because it is expected that the list is used infrequently and its size is small.
* Thus full iterations to do insertions is assumed to be a good compromised to saving memory and tail management
* complexity.
*/
private PendingHandlerCallback pendingHandlerCallbackHead;
/**
* Set to {@code true} once the {@link AbstractChannel} is registered.Once set to {@code true} the value will never
* change.
*/
private boolean registered;
protected DefaultChannelPipeline(Channel channel) {
this.channel = ObjectUtil.checkNotNull(channel, "channel");
@ -163,15 +146,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
addFirst0(newCtx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
@ -211,15 +185,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
addLast0(newCtx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
@ -263,15 +228,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
addBefore0(ctx, newCtx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we add the context to the pipeline and add a task that will call
// ChannelHandler.handlerAdded(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
@ -323,14 +279,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
addAfter0(ctx, newCtx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we remove the context from the pipeline and add a task that will call
// ChannelHandler.handlerRemoved(...) once the channel is registered.
if (!registered) {
newCtx.setAddPending();
callHandlerCallbackLater(newCtx, true);
return this;
}
EventExecutor executor = newCtx.executor();
if (!executor.inEventLoop()) {
newCtx.setAddPending();
@ -483,14 +431,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
synchronized (this) {
remove0(ctx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we remove the context from the pipeline and add a task that will call
// ChannelHandler.handlerRemoved(...) once the channel is registered.
if (!registered) {
callHandlerCallbackLater(ctx, false);
return ctx;
}
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@ -567,15 +507,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
replace0(ctx, newCtx);
// If the registered is false it means that the channel was not registered on an eventloop yet.
// In this case we replace the context in the pipeline
// and add a task that will call ChannelHandler.handlerAdded(...) and
// ChannelHandler.handlerRemoved(...) once the channel is registered.
if (!registered) {
callHandlerCallbackLater(newCtx, true);
callHandlerCallbackLater(ctx, false);
return ctx.handler();
}
EventExecutor executor = ctx.executor();
if (!executor.inEventLoop()) {
executor.execute(new Runnable() {
@ -666,16 +597,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
}
}
final void invokeHandlerAddedIfNeeded() {
assert channel.eventLoop().inEventLoop();
if (firstRegistration) {
firstRegistration = false;
// We are now registered to the EventLoop. It's time to call the callbacks for the ChannelHandlers,
// that were added before the registration was done.
callHandlerAddedForAllHandlers();
}
}
@Override
public final ChannelHandler first() {
ChannelHandlerContext first = firstContext();
@ -992,6 +913,11 @@ public class DefaultChannelPipeline implements ChannelPipeline {
return tail.close();
}
@Override
public final ChannelFuture register() {
return tail.register();
}
@Override
public final ChannelFuture deregister() {
return tail.deregister();
@ -1029,6 +955,11 @@ public class DefaultChannelPipeline implements ChannelPipeline {
return tail.close(promise);
}
@Override
public final ChannelFuture register(final ChannelPromise promise) {
return tail.register(promise);
}
@Override
public final ChannelFuture deregister(final ChannelPromise promise) {
return tail.deregister(promise);
@ -1129,45 +1060,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
}
}
private void callHandlerAddedForAllHandlers() {
final PendingHandlerCallback pendingHandlerCallbackHead;
synchronized (this) {
assert !registered;
// This Channel itself was registered.
registered = true;
pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
// Null out so it can be GC'ed.
this.pendingHandlerCallbackHead = null;
}
// This must happen outside of the synchronized(...) block as otherwise handlerAdded(...) may be called while
// holding the lock and so produce a deadlock if handlerAdded(...) will try to add another handler from outside
// the EventLoop.
PendingHandlerCallback task = pendingHandlerCallbackHead;
while (task != null) {
task.execute();
task = task.next;
}
}
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
assert !registered;
PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
PendingHandlerCallback pending = pendingHandlerCallbackHead;
if (pending == null) {
pendingHandlerCallbackHead = task;
} else {
// Find the tail of the linked-list.
while (pending.next != null) {
pending = pending.next;
}
pending.next = task;
}
}
/**
* Called once a {@link Throwable} hit the end of the {@link ChannelPipeline} without been handled by the user
* in {@link ChannelHandler#exceptionCaught(ChannelHandlerContext, Throwable)}.
@ -1365,6 +1257,11 @@ public class DefaultChannelPipeline implements ChannelPipeline {
unsafe.close(promise);
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
unsafe.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
unsafe.deregister(promise);
@ -1392,7 +1289,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
invokeHandlerAddedIfNeeded();
ctx.fireChannelRegistered();
}
@ -1436,79 +1332,4 @@ public class DefaultChannelPipeline implements ChannelPipeline {
ctx.fireChannelWritabilityChanged();
}
}
private abstract static class PendingHandlerCallback implements Runnable {
final AbstractChannelHandlerContext ctx;
PendingHandlerCallback next;
PendingHandlerCallback(AbstractChannelHandlerContext ctx) {
this.ctx = ctx;
}
abstract void execute();
}
private final class PendingHandlerAddedTask extends PendingHandlerCallback {
PendingHandlerAddedTask(AbstractChannelHandlerContext ctx) {
super(ctx);
}
@Override
public void run() {
callHandlerAdded0(ctx);
}
@Override
void execute() {
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
callHandlerAdded0(ctx);
} else {
try {
executor.execute(this);
} catch (RejectedExecutionException e) {
if (logger.isWarnEnabled()) {
logger.warn(
"Can't invoke handlerAdded() as the EventExecutor {} rejected it, removing handler {}.",
executor, ctx.name(), e);
}
remove0(ctx);
ctx.setRemoved();
}
}
}
}
private final class PendingHandlerRemovedTask extends PendingHandlerCallback {
PendingHandlerRemovedTask(AbstractChannelHandlerContext ctx) {
super(ctx);
}
@Override
public void run() {
callHandlerRemoved0(ctx);
}
@Override
void execute() {
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
callHandlerRemoved0(ctx);
} else {
try {
executor.execute(this);
} catch (RejectedExecutionException e) {
if (logger.isWarnEnabled()) {
logger.warn(
"Can't invoke handlerRemoved() as the EventExecutor {} rejected it," +
" removing handler {}.", executor, ctx.name(), e);
}
// remove0(...) was call before so just call AbstractChannelHandlerContext.setRemoved().
ctx.setRemoved();
}
}
}
}
}

View File

@ -28,25 +28,4 @@ public interface EventLoopGroup extends EventExecutorGroup {
*/
@Override
EventLoop next();
/**
* Register a {@link Channel} with this {@link EventLoop}. The returned {@link ChannelFuture}
* will get notified once the registration was complete.
*/
ChannelFuture register(Channel channel);
/**
* Register a {@link Channel} with this {@link EventLoop} using a {@link ChannelFuture}. The passed
* {@link ChannelFuture} will get notified once the registration was complete and also will get returned.
*/
ChannelFuture register(ChannelPromise promise);
/**
* Register a {@link Channel} with this {@link EventLoop}. The passed {@link ChannelFuture}
* will get notified once the registration was complete and also will get returned.
*
* @deprecated Use {@link #register(ChannelPromise)} instead.
*/
@Deprecated
ChannelFuture register(Channel channel, ChannelPromise promise);
}

View File

@ -80,20 +80,4 @@ public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutor
@Override
protected abstract EventLoop newChild(Executor executor, Object... args) throws Exception;
@Override
public ChannelFuture register(Channel channel) {
return next().register(channel);
}
@Override
public ChannelFuture register(ChannelPromise promise) {
return next().register(promise);
}
@Deprecated
@Override
public ChannelFuture register(Channel channel, ChannelPromise promise) {
return next().register(channel, promise);
}
}

View File

@ -33,9 +33,9 @@ public class ReflectiveChannelFactory<T extends Channel> implements ChannelFacto
}
@Override
public T newChannel() {
public T newChannel(EventLoop eventLoop) throws Exception {
try {
return clazz.getConstructor().newInstance();
return clazz.getConstructor(EventLoop.class).newInstance(eventLoop);
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2018 The Netty Project
*
* 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:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel;
import io.netty.util.internal.StringUtil;
/**
* A {@link ChannelFactory} that instantiates a new {@link ServerChannel} by invoking its default constructor
* reflectively.
*/
public final class ReflectiveServerChannelFactory<T extends ServerChannel> implements ServerChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveServerChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
try {
return clazz.getConstructor(EventLoop.class, EventLoopGroup.class)
.newInstance(eventLoop, childEventLoopGroup);
} catch (Throwable t) {
throw new ChannelException("Unable to create ServerChannel from class " + clazz, t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(clazz) + ".class";
}
}

View File

@ -19,9 +19,13 @@ import io.netty.channel.socket.ServerSocketChannel;
/**
* A {@link Channel} that accepts an incoming connection attempt and creates
* its child {@link Channel}s by accepting them. {@link ServerSocketChannel} is
* its child {@link Channel}s by accepting them. {@link ServerSocketChannel} is
* a good example.
*/
public interface ServerChannel extends Channel {
// This is a tag interface.
/**
* Returns the {@link EventLoopGroup} that is used to register the child {@link Channel}s on.
*/
EventLoopGroup childEventLoopGroup();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2013 The Netty Project
* Copyright 2014 The Netty Project
*
* 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
@ -13,17 +13,14 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.bootstrap;
import io.netty.channel.Channel;
package io.netty.channel;
/**
* @deprecated Use {@link io.netty.channel.ChannelFactory} instead.
* Creates a new {@link ServerChannel}.
*/
@Deprecated
public interface ChannelFactory<T extends Channel> {
public interface ServerChannelFactory<T extends ServerChannel> {
/**
* Creates a new channel.
*/
T newChannel();
T newChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) throws Exception;
}

View File

@ -63,32 +63,6 @@ public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor im
return (EventLoop) super.next();
}
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
@Deprecated
@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
if (channel == null) {
throw new NullPointerException("channel");
}
if (promise == null) {
throw new NullPointerException("promise");
}
channel.unsafe().register(this, promise);
return promise;
}
@Override
protected boolean wakesUpForTask(Runnable task) {
return !(task instanceof NonWakeupRunnable);

View File

@ -59,7 +59,6 @@ public class EmbeddedChannel extends AbstractChannel {
private static final ChannelMetadata METADATA_NO_DISCONNECT = new ChannelMetadata(false);
private static final ChannelMetadata METADATA_DISCONNECT = new ChannelMetadata(true);
private final EmbeddedEventLoop loop = new EmbeddedEventLoop();
private final ChannelFutureListener recordExceptionListener = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
@ -161,7 +160,7 @@ public class EmbeddedChannel extends AbstractChannel {
*/
public EmbeddedChannel(ChannelId channelId, boolean register, boolean hasDisconnect,
final ChannelHandler... handlers) {
super(null, channelId);
super(null, new EmbeddedEventLoop(), channelId);
metadata = metadata(hasDisconnect);
config = new DefaultChannelConfig(this);
setup(register, handlers);
@ -179,7 +178,7 @@ public class EmbeddedChannel extends AbstractChannel {
*/
public EmbeddedChannel(ChannelId channelId, boolean hasDisconnect, final ChannelConfig config,
final ChannelHandler... handlers) {
super(null, channelId);
super(null, new EmbeddedEventLoop(), channelId);
metadata = metadata(hasDisconnect);
this.config = ObjectUtil.checkNotNull(config, "config");
setup(true, handlers);
@ -205,21 +204,25 @@ public class EmbeddedChannel extends AbstractChannel {
}
});
if (register) {
ChannelFuture future = loop.register(this);
ChannelFuture future = register();
assert future.isDone();
}
}
/**
* Register this {@code Channel} on its {@link EventLoop}.
*/
public void register() throws Exception {
ChannelFuture future = loop.register(this);
@Override
public ChannelFuture register() {
return register(newPromise());
}
@Override
public ChannelFuture register(ChannelPromise promise) {
ChannelFuture future = super.register(promise);
assert future.isDone();
Throwable cause = future.cause();
if (cause != null) {
PlatformDependent.throwException(cause);
}
return future;
}
@Override
@ -529,7 +532,7 @@ public class EmbeddedChannel extends AbstractChannel {
runPendingTasks();
if (cancel) {
// Cancel all scheduled tasks that are left.
loop.cancelScheduledTasks();
((EmbeddedEventLoop) eventLoop()).cancelScheduledTasks();
}
}
@ -575,14 +578,15 @@ public class EmbeddedChannel extends AbstractChannel {
* for this {@link Channel}
*/
public void runPendingTasks() {
EmbeddedEventLoop embeddedEventLoop = (EmbeddedEventLoop) eventLoop();
try {
loop.runTasks();
embeddedEventLoop.runTasks();
} catch (Exception e) {
recordException(e);
}
try {
loop.runScheduledTasks();
embeddedEventLoop.runScheduledTasks();
} catch (Exception e) {
recordException(e);
}
@ -594,11 +598,13 @@ public class EmbeddedChannel extends AbstractChannel {
* {@code -1}.
*/
public long runScheduledPendingTasks() {
EmbeddedEventLoop embeddedEventLoop = (EmbeddedEventLoop) eventLoop();
try {
return loop.runScheduledTasks();
return embeddedEventLoop.runScheduledTasks();
} catch (Exception e) {
recordException(e);
return loop.nextScheduledTask();
return embeddedEventLoop.nextScheduledTask();
}
}
@ -770,8 +776,8 @@ public class EmbeddedChannel extends AbstractChannel {
}
@Override
public void register(EventLoop eventLoop, ChannelPromise promise) {
EmbeddedUnsafe.this.register(eventLoop, promise);
public void register(ChannelPromise promise) {
EmbeddedUnsafe.this.register(promise);
runPendingTasks();
}

View File

@ -15,15 +15,10 @@
*/
package io.netty.channel.embedded;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.util.concurrent.AbstractScheduledEventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.ObjectUtil;
import java.util.ArrayDeque;
import java.util.Queue;
@ -119,25 +114,6 @@ final class EmbeddedEventLoop extends AbstractScheduledEventExecutor implements
return false;
}
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
@Deprecated
@Override
public ChannelFuture register(Channel channel, ChannelPromise promise) {
channel.unsafe().register(this, promise);
return promise;
}
@Override
public boolean inEventLoop() {
return true;

View File

@ -91,13 +91,13 @@ public class LocalChannel extends AbstractChannel {
private volatile boolean writeInProgress;
private volatile Future<?> finishReadFuture;
public LocalChannel() {
super(null);
public LocalChannel(EventLoop eventLoop) {
super(null, eventLoop);
config().setAllocator(new PreferHeapByteBufAllocator(config.getAllocator()));
}
protected LocalChannel(LocalServerChannel parent, LocalChannel peer) {
super(parent);
protected LocalChannel(LocalServerChannel parent, EventLoop eventLoop, LocalChannel peer) {
super(parent, eventLoop);
config().setAllocator(new PreferHeapByteBufAllocator(config.getAllocator()));
this.peer = peer;
localAddress = parent.localAddress();
@ -284,6 +284,13 @@ public class LocalChannel extends AbstractChannel {
unsafe().close(unsafe().voidPromise());
} else {
releaseInboundBuffers();
ChannelPromise promise = connectPromise;
if (promise != null) {
// Use tryFailure() instead of setFailure() to avoid the race against cancel().
promise.tryFailure(DO_CLOSE_CLOSED_CHANNEL_EXCEPTION);
connectPromise = null;
}
}
}

View File

@ -20,6 +20,7 @@ import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.PreferHeapByteBufAllocator;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.ServerChannel;
@ -48,7 +49,8 @@ public class LocalServerChannel extends AbstractServerChannel {
private volatile LocalAddress localAddress;
private volatile boolean acceptInProgress;
public LocalServerChannel() {
public LocalServerChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
super(eventLoop, childEventLoopGroup);
config().setAllocator(new PreferHeapByteBufAllocator(config.getAllocator()));
}
@ -166,7 +168,7 @@ public class LocalServerChannel extends AbstractServerChannel {
* to create custom instances of {@link LocalChannel}s.
*/
protected LocalChannel newLocalChannel(LocalChannel peer) {
return new LocalChannel(this, peer);
return new LocalChannel(this, childEventLoopGroup().next(), peer);
}
private void serve0(final LocalChannel child) {

View File

@ -23,6 +23,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.channel.FileRegion;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.internal.ChannelUtils;
@ -60,10 +61,11 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel {
* Create a new instance
*
* @param parent the parent {@link Channel} by which this instance was created. May be {@code null}
* @param eventLoop the {@link EventLoop} to use for IO.
* @param ch the underlying {@link SelectableChannel} on which it operates
*/
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
protected AbstractNioByteChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch) {
super(parent, eventLoop, ch, SelectionKey.OP_READ);
}
/**

View File

@ -77,11 +77,12 @@ public abstract class AbstractNioChannel extends AbstractChannel {
* Create a new instance
*
* @param parent the parent {@link Channel} by which this instance was created. May be {@code null}
* @param eventLoop the {@link EventLoop} to use for all I/O.
* @param ch the underlying {@link SelectableChannel} on which it operates
* @param readInterestOp the ops to set to receive data from the {@link SelectableChannel}
*/
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
protected AbstractNioChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch, int readInterestOp) {
super(parent, eventLoop);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {

View File

@ -19,6 +19,7 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.ServerChannel;
@ -36,10 +37,10 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel {
boolean inputShutdown;
/**
* @see AbstractNioChannel#AbstractNioChannel(Channel, SelectableChannel, int)
* @see AbstractNioChannel#AbstractNioChannel(Channel, EventLoop, SelectableChannel, int)
*/
protected AbstractNioMessageChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent, ch, readInterestOp);
protected AbstractNioMessageChannel(Channel parent, EventLoop eventLoop, SelectableChannel ch, int readInterestOp) {
super(parent, eventLoop, ch, readInterestOp);
}
@Override

View File

@ -630,22 +630,7 @@ public final class NioEventLoop extends SingleThreadEventLoop {
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// If the channel implementation throws an exception because there is no event loop, we ignore this
// because we are only trying to determine if ch is registered to this event loop and thus has authority
// to close ch.
return;
}
// Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
// and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
// still healthy and should not be closed.
// See https://github.com/netty/netty/issues/5125
if (eventLoop != this || eventLoop == null) {
return;
}
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
return;

View File

@ -25,6 +25,7 @@ import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultAddressedEnvelope;
import io.netty.channel.EventLoop;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.nio.AbstractNioMessageChannel;
import io.netty.channel.socket.DatagramChannelConfig;
@ -33,7 +34,6 @@ import io.netty.channel.socket.InternetProtocolFamily;
import io.netty.util.internal.SocketUtils;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.UnstableApi;
import java.io.IOException;
import java.net.InetAddress;
@ -112,24 +112,24 @@ public final class NioDatagramChannel
/**
* Create a new instance which will use the Operation Systems default {@link InternetProtocolFamily}.
*/
public NioDatagramChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
public NioDatagramChannel(EventLoop eventLoop) {
this(eventLoop, newSocket(DEFAULT_SELECTOR_PROVIDER));
}
/**
* Create a new instance using the given {@link SelectorProvider}
* which will use the Operation Systems default {@link InternetProtocolFamily}.
*/
public NioDatagramChannel(SelectorProvider provider) {
this(newSocket(provider));
public NioDatagramChannel(EventLoop eventLoop, SelectorProvider provider) {
this(eventLoop, newSocket(provider));
}
/**
* Create a new instance using the given {@link InternetProtocolFamily}. If {@code null} is used it will depend
* on the Operation Systems default which will be chosen.
*/
public NioDatagramChannel(InternetProtocolFamily ipFamily) {
this(newSocket(DEFAULT_SELECTOR_PROVIDER, ipFamily));
public NioDatagramChannel(EventLoop eventLoop, InternetProtocolFamily ipFamily) {
this(eventLoop, newSocket(DEFAULT_SELECTOR_PROVIDER, ipFamily));
}
/**
@ -137,15 +137,15 @@ public final class NioDatagramChannel
* If {@link InternetProtocolFamily} is {@code null} it will depend on the Operation Systems default
* which will be chosen.
*/
public NioDatagramChannel(SelectorProvider provider, InternetProtocolFamily ipFamily) {
this(newSocket(provider, ipFamily));
public NioDatagramChannel(EventLoop eventLoop, SelectorProvider provider, InternetProtocolFamily ipFamily) {
this(eventLoop, newSocket(provider, ipFamily));
}
/**
* Create a new instance from the given {@link DatagramChannel}.
*/
public NioDatagramChannel(DatagramChannel socket) {
super(null, socket, SelectionKey.OP_READ);
public NioDatagramChannel(EventLoop eventLoop, DatagramChannel socket) {
super(null, eventLoop, socket, SelectionKey.OP_READ);
config = new NioDatagramChannelConfig(this, socket);
}

View File

@ -19,6 +19,9 @@ import io.netty.channel.ChannelException;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.SocketUtils;
import io.netty.channel.nio.AbstractNioMessageChannel;
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
@ -66,29 +69,37 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel
}
private final ServerSocketChannelConfig config;
private final EventLoopGroup childEventLoopGroup;
/**
* Create a new instance
*/
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
this(eventLoop, childEventLoopGroup, newSocket(DEFAULT_SELECTOR_PROVIDER));
}
/**
* Create a new instance using the given {@link SelectorProvider}.
*/
public NioServerSocketChannel(SelectorProvider provider) {
this(newSocket(provider));
public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup, SelectorProvider provider) {
this(eventLoop, childEventLoopGroup, newSocket(provider));
}
/**
* Create a new instance using the given {@link ServerSocketChannel}.
*/
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
public NioServerSocketChannel(
EventLoop eventLoop, EventLoopGroup childEventLoopGroup, ServerSocketChannel channel) {
super(null, eventLoop, channel, SelectionKey.OP_ACCEPT);
this.childEventLoopGroup = ObjectUtil.checkNotNull(childEventLoopGroup, "childEventLoopGroup");
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
@Override
public EventLoopGroup childEventLoopGroup() {
return childEventLoopGroup;
}
@Override
public InetSocketAddress localAddress() {
return (InetSocketAddress) super.localAddress();
@ -144,7 +155,7 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
buf.add(new NioSocketChannel(this, childEventLoopGroup().next(), ch));
return 1;
}
} catch (Throwable t) {

View File

@ -76,32 +76,33 @@ public class NioSocketChannel extends AbstractNioByteChannel implements io.netty
/**
* Create a new instance
*/
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
public NioSocketChannel(EventLoop eventLoop) {
this(eventLoop, DEFAULT_SELECTOR_PROVIDER);
}
/**
* Create a new instance using the given {@link SelectorProvider}.
*/
public NioSocketChannel(SelectorProvider provider) {
this(newSocket(provider));
public NioSocketChannel(EventLoop eventLoop, SelectorProvider provider) {
this(eventLoop, newSocket(provider));
}
/**
* Create a new instance using the given {@link SocketChannel}.
*/
public NioSocketChannel(SocketChannel socket) {
this(null, socket);
public NioSocketChannel(EventLoop eventLoop, SocketChannel socket) {
this(null, eventLoop, socket);
}
/**
* Create a new instance
*
* @param parent the {@link Channel} which created this instance or {@code null} if it was created by the user
* @param eventLoop the {@link EventLoop} to use for IO.
* @param socket the {@link SocketChannel} which will be used
*/
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
public NioSocketChannel(Channel parent, EventLoop eventLoop, SocketChannel socket) {
super(parent, eventLoop, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}

View File

@ -21,13 +21,16 @@ import io.netty.channel.ChannelFactory;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultEventLoop;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.ServerChannelFactory;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalServerChannel;
@ -48,6 +51,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import static org.hamcrest.Matchers.*;
@ -141,16 +145,18 @@ public class BootstrapTest {
@Test
public void testLateRegisterSuccess() throws Exception {
TestEventLoopGroup group = new TestEventLoopGroup();
DefaultEventLoopGroup group = new DefaultEventLoopGroup(1);
try {
LateRegisterHandler registerHandler = new LateRegisterHandler();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(group);
bootstrap.handler(registerHandler);
bootstrap.channel(LocalServerChannel.class);
bootstrap.childHandler(new DummyHandler());
bootstrap.localAddress(new LocalAddress("1"));
ChannelFuture future = bootstrap.bind();
assertFalse(future.isDone());
group.promise.setSuccess();
registerHandler.registerPromise().setSuccess();
final BlockingQueue<Boolean> queue = new LinkedBlockingQueue<Boolean>();
future.addListener(new ChannelFutureListener() {
@Override
@ -169,14 +175,15 @@ public class BootstrapTest {
@Test
public void testLateRegisterSuccessBindFailed() throws Exception {
TestEventLoopGroup group = new TestEventLoopGroup();
EventLoopGroup group = new DefaultEventLoopGroup(1);
LateRegisterHandler registerHandler = new LateRegisterHandler();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(group);
bootstrap.channelFactory(new ChannelFactory<ServerChannel>() {
bootstrap.channelFactory(new ServerChannelFactory<ServerChannel>() {
@Override
public ServerChannel newChannel() {
return new LocalServerChannel() {
public ServerChannel newChannel(EventLoop eventLoop, EventLoopGroup childEventLoopGroup) {
return new LocalServerChannel(eventLoop, childEventLoopGroup) {
@Override
public ChannelFuture bind(SocketAddress localAddress) {
// Close the Channel to emulate what NIO and others impl do on bind failure
@ -196,10 +203,11 @@ public class BootstrapTest {
}
});
bootstrap.childHandler(new DummyHandler());
bootstrap.handler(registerHandler);
bootstrap.localAddress(new LocalAddress("1"));
ChannelFuture future = bootstrap.bind();
assertFalse(future.isDone());
group.promise.setSuccess();
registerHandler.registerPromise().setSuccess();
final BlockingQueue<Boolean> queue = new LinkedBlockingQueue<Boolean>();
future.addListener(new ChannelFutureListener() {
@Override
@ -218,13 +226,17 @@ public class BootstrapTest {
@Test(expected = ConnectException.class, timeout = 10000)
public void testLateRegistrationConnect() throws Exception {
EventLoopGroup group = new DelayedEventLoopGroup();
EventLoopGroup group = new DefaultEventLoopGroup(1);
LateRegisterHandler registerHandler = new LateRegisterHandler();
try {
final Bootstrap bootstrapA = new Bootstrap();
bootstrapA.group(group);
bootstrapA.channel(LocalChannel.class);
bootstrapA.handler(dummyHandler);
bootstrapA.connect(LocalAddress.ANY).syncUninterruptibly();
bootstrapA.handler(registerHandler);
ChannelFuture future = bootstrapA.connect(LocalAddress.ANY);
assertFalse(future.isDone());
registerHandler.registerPromise().setSuccess();
future.syncUninterruptibly();
} finally {
group.shutdownGracefully();
}
@ -280,7 +292,7 @@ public class BootstrapTest {
.group(groupA)
.channelFactory(new ChannelFactory<Channel>() {
@Override
public Channel newChannel() {
public Channel newChannel(EventLoop eventLoop) {
throw exception;
}
});
@ -293,43 +305,30 @@ public class BootstrapTest {
assertThat(connectFuture.channel(), is(not(nullValue())));
}
private static final class DelayedEventLoopGroup extends DefaultEventLoop {
private static final class LateRegisterHandler extends ChannelOutboundHandlerAdapter {
private final CountDownLatch latch = new CountDownLatch(1);
private ChannelPromise registerPromise;
@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
// Delay registration
execute(new Runnable() {
public void register(ChannelHandlerContext ctx, final ChannelPromise promise) throws Exception {
registerPromise = promise;
latch.countDown();
ChannelPromise newPromise = ctx.newPromise();
newPromise.addListener(new ChannelFutureListener() {
@Override
public void run() {
DelayedEventLoopGroup.super.register(channel, promise);
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
registerPromise.tryFailure(future.cause());
}
}
});
return promise;
}
}
private static final class TestEventLoopGroup extends DefaultEventLoopGroup {
ChannelPromise promise;
TestEventLoopGroup() {
super(1);
super.register(ctx, newPromise);
}
@Override
public ChannelFuture register(Channel channel) {
super.register(channel).syncUninterruptibly();
promise = channel.newPromise();
return promise;
}
@Override
public ChannelFuture register(ChannelPromise promise) {
throw new UnsupportedOperationException();
}
@Override
public ChannelFuture register(Channel channel, final ChannelPromise promise) {
throw new UnsupportedOperationException();
ChannelPromise registerPromise() throws InterruptedException {
latch.await();
return registerPromise;
}
}

View File

@ -32,11 +32,11 @@ public class AbstractChannelTest {
// This allows us to have a single-threaded test
when(eventLoop.inEventLoop()).thenReturn(true);
TestChannel channel = new TestChannel();
TestChannel channel = new TestChannel(eventLoop);
ChannelInboundHandler handler = mock(ChannelInboundHandler.class);
channel.pipeline().addLast(handler);
registerChannel(eventLoop, channel);
registerChannel(channel);
verify(handler).handlerAdded(any(ChannelHandlerContext.class));
verify(handler).channelRegistered(any(ChannelHandlerContext.class));
@ -57,15 +57,15 @@ public class AbstractChannelTest {
}
}).when(eventLoop).execute(any(Runnable.class));
final TestChannel channel = new TestChannel();
final TestChannel channel = new TestChannel(eventLoop);
ChannelInboundHandler handler = mock(ChannelInboundHandler.class);
channel.pipeline().addLast(handler);
registerChannel(eventLoop, channel);
registerChannel(channel);
channel.unsafe().deregister(new DefaultChannelPromise(channel));
registerChannel(eventLoop, channel);
registerChannel(channel);
verify(handler).handlerAdded(any(ChannelHandlerContext.class));
@ -77,14 +77,15 @@ public class AbstractChannelTest {
@Test
public void ensureDefaultChannelId() {
TestChannel channel = new TestChannel();
final EventLoop eventLoop = mock(EventLoop.class);
TestChannel channel = new TestChannel(eventLoop);
final ChannelId channelId = channel.id();
assertTrue(channelId instanceof DefaultChannelId);
}
private static void registerChannel(EventLoop eventLoop, Channel channel) throws Exception {
private static void registerChannel(Channel channel) throws Exception {
DefaultChannelPromise future = new DefaultChannelPromise(channel);
channel.unsafe().register(eventLoop, future);
channel.register(future);
future.sync(); // Cause any exceptions to be thrown
}
@ -96,8 +97,8 @@ public class AbstractChannelTest {
public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { }
}
public TestChannel() {
super(null);
public TestChannel(EventLoop eventLoop) {
super(null, eventLoop);
}
@Override

View File

@ -36,31 +36,36 @@ public abstract class AbstractEventLoopTest {
@Test
public void testReregister() {
EventLoopGroup group = newEventLoopGroup();
EventLoopGroup group2 = newEventLoopGroup();
final EventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(2);
ServerBootstrap bootstrap = new ServerBootstrap();
ChannelFuture future = bootstrap.channel(newChannel()).group(group)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
}
}).handler(new ChannelInitializer<ServerSocketChannel>() {
@Override
public void initChannel(ServerSocketChannel ch) throws Exception {
ch.pipeline().addLast(new TestChannelHandler());
ch.pipeline().addLast(eventExecutorGroup, new TestChannelHandler2());
}
})
.bind(0).awaitUninterruptibly();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
ChannelFuture future = bootstrap.channel(newChannel()).group(group)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
}
}).handler(new ChannelInitializer<ServerSocketChannel>() {
@Override
public void initChannel(ServerSocketChannel ch) throws Exception {
ch.pipeline().addLast(new TestChannelHandler());
ch.pipeline().addLast(eventExecutorGroup, new TestChannelHandler2());
}
})
.bind(0).awaitUninterruptibly();
EventExecutor executor = future.channel().pipeline().context(TestChannelHandler2.class).executor();
EventExecutor executor1 = future.channel().pipeline().context(TestChannelHandler.class).executor();
future.channel().deregister().awaitUninterruptibly();
Channel channel = group2.register(future.channel()).awaitUninterruptibly().channel();
EventExecutor executorNew = channel.pipeline().context(TestChannelHandler.class).executor();
assertNotSame(executor1, executorNew);
assertSame(executor, future.channel().pipeline().context(TestChannelHandler2.class).executor());
EventExecutor executor = future.channel().pipeline().context(TestChannelHandler2.class).executor();
EventExecutor executor1 = future.channel().pipeline().context(TestChannelHandler.class).executor();
future.channel().deregister().awaitUninterruptibly();
Channel channel = future.channel().register().awaitUninterruptibly().channel();
EventExecutor executorNew = channel.pipeline().context(TestChannelHandler.class).executor();
assertSame(executor1, executorNew);
assertSame(executor, future.channel().pipeline().context(TestChannelHandler2.class).executor());
} finally {
group.shutdownGracefully();
eventExecutorGroup.shutdownGracefully();
}
}
@Test(timeout = 5000)

View File

@ -27,6 +27,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.nio.channels.ClosedChannelException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@ -84,10 +85,10 @@ public class ChannelInitializerTest {
final Exception exception = new Exception();
final AtomicReference<Throwable> causeRef = new AtomicReference<Throwable>();
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = new LocalChannel(group.next()).pipeline();
if (registerFirst) {
group.register(pipeline.channel()).syncUninterruptibly();
pipeline.channel().register().syncUninterruptibly();
}
pipeline.addFirst(new ChannelInitializer<Channel>() {
@Override
@ -103,7 +104,7 @@ public class ChannelInitializerTest {
});
if (!registerFirst) {
group.register(pipeline.channel()).syncUninterruptibly();
assertTrue(pipeline.channel().register().awaitUninterruptibly().cause() instanceof ClosedChannelException);
}
pipeline.channel().close().syncUninterruptibly();
pipeline.channel().closeFuture().syncUninterruptibly();

View File

@ -137,7 +137,7 @@ public class ChannelOutboundBufferTest {
private final ChannelConfig config = new DefaultChannelConfig(this);
TestChannel() {
super(null);
super(null, new DefaultEventLoop());
}
@Override
@ -147,7 +147,7 @@ public class ChannelOutboundBufferTest {
@Override
protected boolean isCompatible(EventLoop loop) {
return false;
return true;
}
@Override

View File

@ -48,7 +48,8 @@ public class DefaultChannelPipelineTailTest {
@Test
public void testOnUnhandledInboundChannelActive() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
MyChannel myChannel = new MyChannel() {
EventLoop loop = GROUP.next();
MyChannel myChannel = new MyChannel(loop) {
@Override
protected void onUnhandledInboundChannelActive() {
latch.countDown();
@ -57,7 +58,7 @@ public class DefaultChannelPipelineTailTest {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(new MyChannelFactory(myChannel))
.group(GROUP)
.group(loop)
.handler(new ChannelInboundHandlerAdapter())
.remoteAddress(new InetSocketAddress(0));
@ -74,7 +75,8 @@ public class DefaultChannelPipelineTailTest {
@Test
public void testOnUnhandledInboundChannelInactive() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
MyChannel myChannel = new MyChannel() {
EventLoop loop = GROUP.next();
MyChannel myChannel = new MyChannel(loop) {
@Override
protected void onUnhandledInboundChannelInactive() {
latch.countDown();
@ -83,7 +85,7 @@ public class DefaultChannelPipelineTailTest {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(new MyChannelFactory(myChannel))
.group(GROUP)
.group(loop)
.handler(new ChannelInboundHandlerAdapter())
.remoteAddress(new InetSocketAddress(0));
@ -99,7 +101,8 @@ public class DefaultChannelPipelineTailTest {
public void testOnUnhandledInboundException() throws Exception {
final AtomicReference<Throwable> causeRef = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
MyChannel myChannel = new MyChannel() {
EventLoop loop = GROUP.next();
MyChannel myChannel = new MyChannel(loop) {
@Override
protected void onUnhandledInboundException(Throwable cause) {
causeRef.set(cause);
@ -109,7 +112,7 @@ public class DefaultChannelPipelineTailTest {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(new MyChannelFactory(myChannel))
.group(GROUP)
.group(loop)
.handler(new ChannelInboundHandlerAdapter())
.remoteAddress(new InetSocketAddress(0));
@ -129,7 +132,8 @@ public class DefaultChannelPipelineTailTest {
@Test
public void testOnUnhandledInboundMessage() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
MyChannel myChannel = new MyChannel() {
EventLoop loop = GROUP.next();
MyChannel myChannel = new MyChannel(loop) {
@Override
protected void onUnhandledInboundMessage(Object msg) {
latch.countDown();
@ -138,7 +142,7 @@ public class DefaultChannelPipelineTailTest {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(new MyChannelFactory(myChannel))
.group(GROUP)
.group(loop)
.handler(new ChannelInboundHandlerAdapter())
.remoteAddress(new InetSocketAddress(0));
@ -156,7 +160,8 @@ public class DefaultChannelPipelineTailTest {
@Test
public void testOnUnhandledInboundReadComplete() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
MyChannel myChannel = new MyChannel() {
EventLoop loop = GROUP.next();
MyChannel myChannel = new MyChannel(loop) {
@Override
protected void onUnhandledInboundReadComplete() {
latch.countDown();
@ -165,7 +170,7 @@ public class DefaultChannelPipelineTailTest {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(new MyChannelFactory(myChannel))
.group(GROUP)
.group(loop)
.handler(new ChannelInboundHandlerAdapter())
.remoteAddress(new InetSocketAddress(0));
@ -183,7 +188,8 @@ public class DefaultChannelPipelineTailTest {
@Test
public void testOnUnhandledInboundUserEventTriggered() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
MyChannel myChannel = new MyChannel() {
EventLoop loop = GROUP.next();
MyChannel myChannel = new MyChannel(loop) {
@Override
protected void onUnhandledInboundUserEventTriggered(Object evt) {
latch.countDown();
@ -192,7 +198,7 @@ public class DefaultChannelPipelineTailTest {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(new MyChannelFactory(myChannel))
.group(GROUP)
.group(loop)
.handler(new ChannelInboundHandlerAdapter())
.remoteAddress(new InetSocketAddress(0));
@ -210,7 +216,8 @@ public class DefaultChannelPipelineTailTest {
@Test
public void testOnUnhandledInboundWritabilityChanged() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
MyChannel myChannel = new MyChannel() {
EventLoop loop = GROUP.next();
MyChannel myChannel = new MyChannel(loop) {
@Override
protected void onUnhandledInboundWritabilityChanged() {
latch.countDown();
@ -219,7 +226,7 @@ public class DefaultChannelPipelineTailTest {
Bootstrap bootstrap = new Bootstrap()
.channelFactory(new MyChannelFactory(myChannel))
.group(GROUP)
.group(loop)
.handler(new ChannelInboundHandlerAdapter())
.remoteAddress(new InetSocketAddress(0));
@ -242,7 +249,7 @@ public class DefaultChannelPipelineTailTest {
}
@Override
public MyChannel newChannel() {
public MyChannel newChannel(EventLoop eventLoop) {
return channel;
}
}
@ -255,8 +262,8 @@ public class DefaultChannelPipelineTailTest {
private boolean active;
private boolean closed;
protected MyChannel() {
super(null);
protected MyChannel(EventLoop eventLoop) {
super(null, eventLoop);
}
@Override

View File

@ -24,7 +24,6 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel;
import io.netty.channel.local.LocalEventLoopGroup;
import io.netty.channel.local.LocalServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
@ -161,9 +160,13 @@ public class DefaultChannelPipelineTest {
}
}
private static LocalChannel newLocalChannel() {
return new LocalChannel(group.next());
}
@Test
public void testRemoveChannelHandler() {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
ChannelHandler handler1 = newHandler();
ChannelHandler handler2 = newHandler();
@ -186,7 +189,7 @@ public class DefaultChannelPipelineTest {
@Test
public void testRemoveIfExists() {
DefaultChannelPipeline pipeline = new DefaultChannelPipeline(new LocalChannel());
DefaultChannelPipeline pipeline = new DefaultChannelPipeline(newLocalChannel());
ChannelHandler handler1 = newHandler();
ChannelHandler handler2 = newHandler();
@ -208,7 +211,7 @@ public class DefaultChannelPipelineTest {
@Test
public void testRemoveIfExistsDoesNotThrowException() {
DefaultChannelPipeline pipeline = new DefaultChannelPipeline(new LocalChannel());
DefaultChannelPipeline pipeline = new DefaultChannelPipeline(newLocalChannel());
ChannelHandler handler1 = newHandler();
ChannelHandler handler2 = newHandler();
@ -222,7 +225,7 @@ public class DefaultChannelPipelineTest {
@Test(expected = NoSuchElementException.class)
public void testRemoveThrowNoSuchElementException() {
DefaultChannelPipeline pipeline = new DefaultChannelPipeline(new LocalChannel());
DefaultChannelPipeline pipeline = new DefaultChannelPipeline(newLocalChannel());
ChannelHandler handler1 = newHandler();
pipeline.addLast("handler1", handler1);
@ -232,7 +235,7 @@ public class DefaultChannelPipelineTest {
@Test
public void testReplaceChannelHandler() {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
ChannelHandler handler1 = newHandler();
pipeline.addLast("handler1", handler1);
@ -257,7 +260,7 @@ public class DefaultChannelPipelineTest {
@Test
public void testChannelHandlerContextNavigation() {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
final int HANDLER_ARRAY_LEN = 5;
ChannelHandler[] firstHandlers = newHandlers(HANDLER_ARRAY_LEN);
@ -272,7 +275,7 @@ public class DefaultChannelPipelineTest {
@Test
public void testFireChannelRegistered() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addLast(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
@ -284,13 +287,13 @@ public class DefaultChannelPipelineTest {
});
}
});
group.register(pipeline.channel());
pipeline.channel().register();
assertTrue(latch.await(2, TimeUnit.SECONDS));
}
@Test
public void testPipelineOperation() {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
final int handlerNum = 5;
ChannelHandler[] handlers1 = newHandlers(handlerNum);
@ -318,7 +321,7 @@ public class DefaultChannelPipelineTest {
@Test
public void testChannelHandlerContextOrder() {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addFirst("1", newHandler());
pipeline.addLast("10", newHandler());
@ -515,8 +518,8 @@ public class DefaultChannelPipelineTest {
// Tests for https://github.com/netty/netty/issues/2349
@Test
public void testCancelBind() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel());
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register();
ChannelPromise promise = pipeline.channel().newPromise();
assertTrue(promise.cancel(false));
@ -526,8 +529,8 @@ public class DefaultChannelPipelineTest {
@Test
public void testCancelConnect() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel());
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register();
ChannelPromise promise = pipeline.channel().newPromise();
assertTrue(promise.cancel(false));
@ -537,8 +540,8 @@ public class DefaultChannelPipelineTest {
@Test
public void testCancelDisconnect() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel());
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register();
ChannelPromise promise = pipeline.channel().newPromise();
assertTrue(promise.cancel(false));
@ -548,8 +551,8 @@ public class DefaultChannelPipelineTest {
@Test
public void testCancelClose() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel());
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register();
ChannelPromise promise = pipeline.channel().newPromise();
assertTrue(promise.cancel(false));
@ -559,11 +562,11 @@ public class DefaultChannelPipelineTest {
@Test(expected = IllegalArgumentException.class)
public void testWrongPromiseChannel() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel()).sync();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register().sync();
ChannelPipeline pipeline2 = new LocalChannel().pipeline();
group.register(pipeline2.channel()).sync();
ChannelPipeline pipeline2 = newLocalChannel().pipeline();
pipeline2.channel().register().sync();
try {
ChannelPromise promise2 = pipeline2.channel().newPromise();
@ -576,8 +579,8 @@ public class DefaultChannelPipelineTest {
@Test(expected = IllegalArgumentException.class)
public void testUnexpectedVoidChannelPromise() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel()).sync();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register().sync();
try {
ChannelPromise promise = new VoidChannelPromise(pipeline.channel(), false);
@ -589,8 +592,8 @@ public class DefaultChannelPipelineTest {
@Test(expected = IllegalArgumentException.class)
public void testUnexpectedVoidChannelPromiseCloseFuture() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel()).sync();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register().sync();
try {
ChannelPromise promise = (ChannelPromise) pipeline.channel().closeFuture();
@ -602,8 +605,8 @@ public class DefaultChannelPipelineTest {
@Test
public void testCancelDeregister() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel());
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register().sync();
ChannelPromise promise = pipeline.channel().newPromise();
assertTrue(promise.cancel(false));
@ -613,8 +616,8 @@ public class DefaultChannelPipelineTest {
@Test
public void testCancelWrite() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel());
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register().sync();
ChannelPromise promise = pipeline.channel().newPromise();
assertTrue(promise.cancel(false));
@ -627,8 +630,8 @@ public class DefaultChannelPipelineTest {
@Test
public void testCancelWriteAndFlush() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel());
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().register().sync();
ChannelPromise promise = pipeline.channel().newPromise();
assertTrue(promise.cancel(false));
@ -641,25 +644,25 @@ public class DefaultChannelPipelineTest {
@Test
public void testFirstContextEmptyPipeline() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
assertNull(pipeline.firstContext());
}
@Test
public void testLastContextEmptyPipeline() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
assertNull(pipeline.lastContext());
}
@Test
public void testFirstHandlerEmptyPipeline() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
assertNull(pipeline.first());
}
@Test
public void testLastHandlerEmptyPipeline() throws Exception {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
assertNull(pipeline.last());
}
@ -668,7 +671,7 @@ public class DefaultChannelPipelineTest {
final IllegalStateException exception = new IllegalStateException();
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
EmbeddedChannel channel = new EmbeddedChannel(new ChannelInitializer<Channel>() {
EmbeddedChannel channel = new EmbeddedChannel(false, false, new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
throw exception;
@ -690,7 +693,7 @@ public class DefaultChannelPipelineTest {
public void testChannelUnregistrationWithCustomExecutor() throws Exception {
final CountDownLatch channelLatch = new CountDownLatch(1);
final CountDownLatch handlerLatch = new CountDownLatch(1);
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addLast(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
@ -710,7 +713,7 @@ public class DefaultChannelPipelineTest {
}
});
Channel channel = pipeline.channel();
group.register(channel);
channel.register().sync();
channel.close();
channel.deregister();
assertTrue(channelLatch.await(2, TimeUnit.SECONDS));
@ -722,13 +725,14 @@ public class DefaultChannelPipelineTest {
final EventLoop loop = group.next();
CheckEventExecutorHandler handler = new CheckEventExecutorHandler(loop);
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addFirst(handler);
assertFalse(handler.addedPromise.isDone());
group.register(pipeline.channel());
handler.addedPromise.syncUninterruptibly();
pipeline.channel().register();
pipeline.remove(handler);
handler.removedPromise.syncUninterruptibly();
pipeline.channel().close().syncUninterruptibly();
}
@Test(timeout = 3000)
@ -737,11 +741,10 @@ public class DefaultChannelPipelineTest {
final CountDownLatch latch = new CountDownLatch(1);
CheckEventExecutorHandler handler = new CheckEventExecutorHandler(loop);
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addFirst(handler);
assertFalse(handler.addedPromise.isDone());
group.register(pipeline.channel());
handler.addedPromise.syncUninterruptibly();
pipeline.channel().register();
pipeline.replace(handler, null, new ChannelHandlerAdapter() {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
@ -750,34 +753,8 @@ public class DefaultChannelPipelineTest {
});
handler.removedPromise.syncUninterruptibly();
latch.await();
}
@Test
public void testAddRemoveHandlerNotRegistered() throws Throwable {
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
ChannelHandler handler = new ErrorChannelHandler(error);
ChannelPipeline pipeline = new LocalChannel().pipeline();
pipeline.addFirst(handler);
pipeline.remove(handler);
Throwable cause = error.get();
if (cause != null) {
throw cause;
}
}
@Test
public void testAddReplaceHandlerNotRegistered() throws Throwable {
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
ChannelHandler handler = new ErrorChannelHandler(error);
ChannelPipeline pipeline = new LocalChannel().pipeline();
pipeline.addFirst(handler);
pipeline.replace(handler, null, new ErrorChannelHandler(error));
Throwable cause = error.get();
if (cause != null) {
throw cause;
}
pipeline.channel().close().syncUninterruptibly();
}
@Test(timeout = 3000)
@ -794,9 +771,9 @@ public class DefaultChannelPipelineTest {
CheckOrderHandler handler3 = new CheckOrderHandler(addedQueue, removedQueue);
CheckOrderHandler handler4 = new CheckOrderHandler(addedQueue, removedQueue);
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addLast(handler1);
group.register(pipeline.channel()).syncUninterruptibly();
pipeline.channel().register().syncUninterruptibly();
pipeline.addLast(group1, handler2);
pipeline.addLast(group2, handler3);
pipeline.addLast(handler4);
@ -828,20 +805,18 @@ public class DefaultChannelPipelineTest {
final EventExecutorGroup group1 = new DefaultEventExecutorGroup(1);
try {
final Promise<Void> promise = group1.next().newPromise();
final AtomicBoolean handlerAdded = new AtomicBoolean();
final Exception exception = new RuntimeException();
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addLast(group1, new CheckExceptionHandler(exception, promise));
pipeline.addFirst(new ChannelHandlerAdapter() {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
handlerAdded.set(true);
throw exception;
}
});
assertFalse(handlerAdded.get());
group.register(pipeline.channel());
pipeline.channel().register();
promise.syncUninterruptibly();
pipeline.channel().close().syncUninterruptibly();
} finally {
group1.shutdownGracefully();
}
@ -854,7 +829,7 @@ public class DefaultChannelPipelineTest {
final Promise<Void> promise = group1.next().newPromise();
String handlerName = "foo";
final Exception exception = new RuntimeException();
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addLast(handlerName, new ChannelHandlerAdapter() {
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
@ -862,9 +837,10 @@ public class DefaultChannelPipelineTest {
}
});
pipeline.addLast(group1, new CheckExceptionHandler(exception, promise));
group.register(pipeline.channel()).syncUninterruptibly();
pipeline.channel().register().syncUninterruptibly();
pipeline.remove(handlerName);
promise.syncUninterruptibly();
pipeline.channel().close().syncUninterruptibly();
} finally {
group1.shutdownGracefully();
}
@ -879,7 +855,7 @@ public class DefaultChannelPipelineTest {
final Exception exceptionAdded = new RuntimeException();
final Exception exceptionRemoved = new RuntimeException();
String handlerName = "foo";
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addLast(group1, new CheckExceptionHandler(exceptionAdded, promise));
pipeline.addFirst(handlerName, new ChannelHandlerAdapter() {
@Override
@ -899,82 +875,79 @@ public class DefaultChannelPipelineTest {
throw exceptionRemoved;
}
});
group.register(pipeline.channel()).syncUninterruptibly();
pipeline.register().syncUninterruptibly();
latch.await();
assertNull(pipeline.context(handlerName));
promise.syncUninterruptibly();
pipeline.channel().close().syncUninterruptibly();
} finally {
group1.shutdownGracefully();
}
}
@Test(timeout = 2000)
public void testAddRemoveHandlerCalledOnceRegistered() throws Throwable {
ChannelPipeline pipeline = new LocalChannel().pipeline();
public void testAddRemoveHandlerCalled() throws Throwable {
ChannelPipeline pipeline = newLocalChannel().pipeline();
CallbackCheckHandler handler = new CallbackCheckHandler();
pipeline.addFirst(handler);
pipeline.remove(handler);
assertNull(handler.addedHandler.getNow());
assertNull(handler.removedHandler.getNow());
assertTrue(handler.addedHandler.get());
assertTrue(handler.removedHandler.get());
group.register(pipeline.channel()).syncUninterruptibly();
pipeline.channel().register().syncUninterruptibly();
Throwable cause = handler.error.get();
pipeline.channel().close().syncUninterruptibly();
if (cause != null) {
throw cause;
}
assertTrue(handler.addedHandler.get());
assertTrue(handler.removedHandler.get());
}
@Test(timeout = 3000)
public void testAddReplaceHandlerCalledOnceRegistered() throws Throwable {
ChannelPipeline pipeline = new LocalChannel().pipeline();
public void testAddReplaceHandlerCalled() throws Throwable {
ChannelPipeline pipeline = newLocalChannel().pipeline();
CallbackCheckHandler handler = new CallbackCheckHandler();
CallbackCheckHandler handler2 = new CallbackCheckHandler();
pipeline.addFirst(handler);
pipeline.replace(handler, null, handler2);
assertNull(handler.addedHandler.getNow());
assertNull(handler.removedHandler.getNow());
assertNull(handler2.addedHandler.getNow());
assertTrue(handler.addedHandler.get());
assertTrue(handler.removedHandler.get());
assertTrue(handler2.addedHandler.get());
assertNull(handler2.removedHandler.getNow());
group.register(pipeline.channel()).syncUninterruptibly();
pipeline.channel().register().syncUninterruptibly();
Throwable cause = handler.error.get();
if (cause != null) {
throw cause;
}
assertTrue(handler.addedHandler.get());
assertTrue(handler.removedHandler.get());
Throwable cause2 = handler2.error.get();
if (cause2 != null) {
throw cause2;
}
assertTrue(handler2.addedHandler.get());
assertNull(handler2.removedHandler.getNow());
pipeline.remove(handler2);
assertTrue(handler2.removedHandler.get());
pipeline.channel().close().syncUninterruptibly();
}
@Test(timeout = 3000)
public void testAddBefore() throws Throwable {
ChannelPipeline pipeline1 = new LocalChannel().pipeline();
ChannelPipeline pipeline2 = new LocalChannel().pipeline();
EventLoopGroup defaultGroup = new DefaultEventLoopGroup(2);
try {
EventLoop eventLoop1 = defaultGroup.next();
EventLoop eventLoop2 = defaultGroup.next();
eventLoop1.register(pipeline1.channel()).syncUninterruptibly();
eventLoop2.register(pipeline2.channel()).syncUninterruptibly();
ChannelPipeline pipeline1 = new LocalChannel(eventLoop1).pipeline();
ChannelPipeline pipeline2 = new LocalChannel(eventLoop2).pipeline();
pipeline1.channel().register().syncUninterruptibly();
pipeline2.channel().register().syncUninterruptibly();
CountDownLatch latch = new CountDownLatch(2 * 10);
for (int i = 0; i < 10; i++) {
@ -982,6 +955,8 @@ public class DefaultChannelPipelineTest {
eventLoop2.execute(new TestTask(pipeline1, latch));
}
latch.await();
pipeline1.channel().close().syncUninterruptibly();
pipeline2.channel().close().syncUninterruptibly();
} finally {
defaultGroup.shutdownGracefully();
}
@ -989,20 +964,25 @@ public class DefaultChannelPipelineTest {
@Test(timeout = 3000)
public void testAddInListenerNio() throws Throwable {
testAddInListener(new NioSocketChannel(), new NioEventLoopGroup(1));
NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1);
try {
testAddInListener(new NioSocketChannel(nioEventLoopGroup.next()));
} finally {
nioEventLoopGroup.shutdownGracefully();
}
}
@Test(timeout = 3000)
public void testAddInListenerLocal() throws Throwable {
testAddInListener(new LocalChannel(), new DefaultEventLoopGroup(1));
testAddInListener(newLocalChannel());
}
private static void testAddInListener(Channel channel, EventLoopGroup group) throws Throwable {
private static void testAddInListener(Channel channel) throws Throwable {
ChannelPipeline pipeline1 = channel.pipeline();
try {
final Object event = new Object();
final Promise<Object> promise = ImmediateEventExecutor.INSTANCE.newPromise();
group.register(pipeline1.channel()).addListener(new ChannelFutureListener() {
pipeline1.channel().register().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
ChannelPipeline pipeline = future.channel().pipeline();
@ -1034,13 +1014,12 @@ public class DefaultChannelPipelineTest {
assertSame(event, promise.syncUninterruptibly().getNow());
} finally {
pipeline1.channel().close().syncUninterruptibly();
group.shutdownGracefully();
}
}
@Test
public void testNullName() {
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.addLast(newHandler());
pipeline.addLast(null, newHandler());
pipeline.addFirst(newHandler());
@ -1054,12 +1033,13 @@ public class DefaultChannelPipelineTest {
@Test(timeout = 3000)
public void testUnorderedEventExecutor() throws Throwable {
ChannelPipeline pipeline1 = new LocalChannel().pipeline();
EventExecutorGroup eventExecutors = new UnorderedThreadPoolEventExecutor(2);
EventLoopGroup defaultGroup = new DefaultEventLoopGroup(1);
try {
EventLoop eventLoop1 = defaultGroup.next();
eventLoop1.register(pipeline1.channel()).syncUninterruptibly();
ChannelPipeline pipeline1 = new LocalChannel(eventLoop1).pipeline();
pipeline1.channel().register().syncUninterruptibly();
final CountDownLatch latch = new CountDownLatch(1);
pipeline1.addLast(eventExecutors, new ChannelInboundHandlerAdapter() {
@Override
@ -1086,8 +1066,8 @@ public class DefaultChannelPipelineTest {
@Test
public void testPinExecutor() {
EventExecutorGroup group = new DefaultEventExecutorGroup(2);
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline2 = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
ChannelPipeline pipeline2 = newLocalChannel().pipeline();
pipeline.addLast(group, "h1", new ChannelInboundHandlerAdapter());
pipeline.addLast(group, "h2", new ChannelInboundHandlerAdapter());
@ -1107,7 +1087,7 @@ public class DefaultChannelPipelineTest {
@Test
public void testNotPinExecutor() {
EventExecutorGroup group = new DefaultEventExecutorGroup(2);
ChannelPipeline pipeline = new LocalChannel().pipeline();
ChannelPipeline pipeline = newLocalChannel().pipeline();
pipeline.channel().config().setOption(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP, false);
pipeline.addLast(group, "h1", new ChannelInboundHandlerAdapter());
@ -1123,14 +1103,14 @@ public class DefaultChannelPipelineTest {
@Test(timeout = 3000)
public void testVoidPromiseNotify() throws Throwable {
ChannelPipeline pipeline1 = new LocalChannel().pipeline();
EventLoopGroup defaultGroup = new DefaultEventLoopGroup(1);
EventLoop eventLoop1 = defaultGroup.next();
ChannelPipeline pipeline1 = new LocalChannel(eventLoop1).pipeline();
final Promise<Throwable> promise = eventLoop1.newPromise();
final Exception exception = new IllegalArgumentException();
try {
eventLoop1.register(pipeline1.channel()).syncUninterruptibly();
pipeline1.channel().register().syncUninterruptibly();
pipeline1.addLast(new ChannelDuplexHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
@ -1161,8 +1141,8 @@ public class DefaultChannelPipelineTest {
// the time.
for (int i = 0; i < 500; i++) {
ChannelPipeline pipeline = new LocalChannel().pipeline();
group.register(pipeline.channel()).sync();
ChannelPipeline pipeline = new LocalChannel(group.next()).pipeline();
pipeline.channel().register().sync();
final CountDownLatch latch = new CountDownLatch(1);
@ -1228,13 +1208,11 @@ public class DefaultChannelPipelineTest {
private static void handlerAddedStateUpdatedBeforeHandlerAddedDone(boolean executeInEventLoop)
throws InterruptedException {
final ChannelPipeline pipeline = new LocalChannel().pipeline();
final ChannelPipeline pipeline = newLocalChannel().pipeline();
final Object userEvent = new Object();
final Object writeObject = new Object();
final CountDownLatch doneLatch = new CountDownLatch(1);
group.register(pipeline.channel());
Runnable r = new Runnable() {
@Override
public void run() {

View File

@ -21,7 +21,7 @@ import java.util.EnumSet;
final class LoggingHandler implements ChannelInboundHandler, ChannelOutboundHandler {
enum Event { WRITE, FLUSH, BIND, CONNECT, DISCONNECT, CLOSE, DEREGISTER, READ, WRITABILITY,
enum Event { WRITE, FLUSH, BIND, CONNECT, DISCONNECT, CLOSE, REGISTER, DEREGISTER, READ, WRITABILITY,
HANDLER_ADDED, HANDLER_REMOVED, EXCEPTION, READ_COMPLETE, REGISTERED, UNREGISTERED, ACTIVE, INACTIVE,
USER }
@ -67,6 +67,12 @@ final class LoggingHandler implements ChannelInboundHandler, ChannelOutboundHand
ctx.close(promise);
}
@Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
log(Event.REGISTER);
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
log(Event.DEREGISTER);

View File

@ -357,11 +357,13 @@ public class SingleThreadEventLoopTest {
}
try {
ChannelFuture f = loopA.register(new LocalChannel());
Channel channel = new LocalChannel(loopA);
ChannelFuture f = channel.register();
f.awaitUninterruptibly();
assertFalse(f.isSuccess());
assertThat(f.cause(), is(instanceOf(RejectedExecutionException.class)));
assertFalse(f.channel().isOpen());
// TODO: What to do in this case ?
//assertFalse(f.channel().isOpen());
} finally {
for (Appender<ILoggingEvent> a: appenders) {
root.addAppender(a);
@ -374,7 +376,7 @@ public class SingleThreadEventLoopTest {
public void testRegistrationAfterShutdown2() throws Exception {
loopA.shutdown();
final CountDownLatch latch = new CountDownLatch(1);
Channel ch = new LocalChannel();
Channel ch = new LocalChannel(loopA);
ChannelPromise promise = ch.newPromise();
promise.addListener(new ChannelFutureListener() {
@Override
@ -393,14 +395,15 @@ public class SingleThreadEventLoopTest {
}
try {
ChannelFuture f = loopA.register(promise);
ChannelFuture f = ch.register(promise);
f.awaitUninterruptibly();
assertFalse(f.isSuccess());
assertThat(f.cause(), is(instanceOf(RejectedExecutionException.class)));
// Ensure the listener was notified.
assertFalse(latch.await(1, TimeUnit.SECONDS));
assertFalse(ch.isOpen());
// TODO: What to do in this case ?
//assertFalse(ch.isOpen());
} finally {
for (Appender<ILoggingEvent> a: appenders) {
root.addAppender(a);

View File

@ -283,7 +283,7 @@ public class LocalChannelTest {
}
});
ChannelFuture future = bootstrap.connect(sc.localAddress());
assertTrue("Connection should finish, not time out", future.await(200));
assertTrue("Connection should finish, not time out", future.await(2000));
cc = future.channel();
} finally {
closeChannel(cc);

View File

@ -92,7 +92,7 @@ public class LocalTransportThreadModelTest {
ThreadNameAuditor h2 = new ThreadNameAuditor();
ThreadNameAuditor h3 = new ThreadNameAuditor(true);
Channel ch = new LocalChannel();
Channel ch = new LocalChannel(l.next());
// With no EventExecutor specified, h1 will be always invoked by EventLoop 'l'.
ch.pipeline().addLast(h1);
// h2 will be always invoked by EventExecutor 'e1'.
@ -100,7 +100,7 @@ public class LocalTransportThreadModelTest {
// h3 will be always invoked by EventExecutor 'e2'.
ch.pipeline().addLast(e2, h3);
l.register(ch).sync().channel().connect(localAddr).sync();
ch.register().sync().channel().connect(localAddr).sync();
// Fire inbound events from all possible starting points.
ch.pipeline().fireChannelRead("1");
@ -243,7 +243,7 @@ public class LocalTransportThreadModelTest {
final MessageForwarder2 h5 = new MessageForwarder2();
final MessageDiscarder h6 = new MessageDiscarder();
final Channel ch = new LocalChannel();
final Channel ch = new LocalChannel(l.next());
// inbound: int -> byte[4] -> int -> int -> byte[4] -> int -> /dev/null
// outbound: int -> int -> byte[4] -> int -> int -> byte[4] -> /dev/null
@ -254,7 +254,7 @@ public class LocalTransportThreadModelTest {
.addLast(e4, h5)
.addLast(e5, h6);
l.register(ch).sync().channel().connect(localAddr).sync();
ch.register().sync().channel().connect(localAddr).sync();
final int ROUNDS = 1024;
final int ELEMS_PER_ROUNDS = 8192;

View File

@ -134,7 +134,7 @@ public class LocalTransportThreadModelTest3 {
final EventForwarder h5 = new EventForwarder();
final EventRecorder h6 = new EventRecorder(events, inbound);
final Channel ch = new LocalChannel();
final Channel ch = new LocalChannel(l.next());
if (!inbound) {
ch.config().setAutoRead(false);
}
@ -145,7 +145,7 @@ public class LocalTransportThreadModelTest3 {
.addLast(e1, h5)
.addLast(e1, "recorder", h6);
l.register(ch).sync().channel().connect(localAddr).sync();
ch.register().sync().channel().connect(localAddr).sync();
final LinkedList<EventType> expectedEvents = events(inbound, 8192);

View File

@ -59,8 +59,8 @@ public class NioEventLoopTest extends AbstractEventLoopTest {
EventLoopGroup group = new NioEventLoopGroup(1);
final NioEventLoop loop = (NioEventLoop) group.next();
try {
Channel channel = new NioServerSocketChannel();
loop.register(channel).syncUninterruptibly();
Channel channel = new NioServerSocketChannel(loop, group);
channel.register().syncUninterruptibly();
Selector selector = loop.unwrappedSelector();
assertSame(selector, ((NioEventLoop) channel.eventLoop()).unwrappedSelector());
@ -151,8 +151,8 @@ public class NioEventLoopTest extends AbstractEventLoopTest {
NioEventLoop loop = (NioEventLoop) group.next();
try {
Channel channel = new NioServerSocketChannel();
loop.register(channel).syncUninterruptibly();
Channel channel = new NioServerSocketChannel(loop, group);
channel.register().syncUninterruptibly();
channel.bind(new InetSocketAddress(0)).syncUninterruptibly();
SocketChannel selectableChannel = SocketChannel.open();
@ -242,10 +242,10 @@ public class NioEventLoopTest extends AbstractEventLoopTest {
SelectorProvider.provider(), selectStrategyFactory);
final NioEventLoop loop = (NioEventLoop) group.next();
try {
Channel channel = new NioServerSocketChannel();
Channel channel = new NioServerSocketChannel(loop, group);
Selector selector = loop.unwrappedSelector();
loop.register(channel).syncUninterruptibly();
channel.register().syncUninterruptibly();
Selector newSelector = ((NioEventLoop) channel.eventLoop()).unwrappedSelector();
assertTrue(newSelector.isOpen());

View File

@ -16,7 +16,9 @@
package io.netty.channel.socket.nio;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.AbstractNioChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import org.junit.Test;
import java.io.IOException;
@ -28,7 +30,7 @@ import static org.junit.Assert.*;
public abstract class AbstractNioChannelTest<T extends AbstractNioChannel> {
protected abstract T newNioChannel();
protected abstract T newNioChannel(EventLoopGroup group);
protected abstract NetworkChannel jdkChannel(T channel);
@ -36,7 +38,8 @@ public abstract class AbstractNioChannelTest<T extends AbstractNioChannel> {
@Test
public void testNioChannelOption() throws IOException {
T channel = newNioChannel();
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
T channel = newNioChannel(eventLoopGroup);
try {
NetworkChannel jdkChannel = jdkChannel(channel);
ChannelOption<Boolean> option = NioChannelOption.of(StandardSocketOptions.SO_REUSEADDR);
@ -51,29 +54,34 @@ public abstract class AbstractNioChannelTest<T extends AbstractNioChannel> {
assertEquals(value3, value4);
assertNotEquals(value1, value4);
} finally {
channel.unsafe().closeForcibly();
channel.close().syncUninterruptibly();
eventLoopGroup.shutdownGracefully();
}
}
@Test
public void testInvalidNioChannelOption() {
T channel = newNioChannel();
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
T channel = newNioChannel(eventLoopGroup);
try {
ChannelOption<?> option = NioChannelOption.of(newInvalidOption());
assertFalse(channel.config().setOption(option, null));
assertNull(channel.config().getOption(option));
} finally {
channel.unsafe().closeForcibly();
channel.close().syncUninterruptibly();
eventLoopGroup.shutdownGracefully();
}
}
@Test
public void testGetOptions() {
T channel = newNioChannel();
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
T channel = newNioChannel(eventLoopGroup);
try {
channel.config().getOptions();
} finally {
channel.unsafe().closeForcibly();
channel.close().syncUninterruptibly();
eventLoopGroup.shutdownGracefully();
}
}
}

View File

@ -19,6 +19,7 @@ import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
@ -66,8 +67,8 @@ public class NioDatagramChannelTest extends AbstractNioChannelTest<NioDatagramCh
}
@Override
protected NioDatagramChannel newNioChannel() {
return new NioDatagramChannel();
protected NioDatagramChannel newNioChannel(EventLoopGroup group) {
return new NioDatagramChannel(group.next());
}
@Override

View File

@ -32,10 +32,11 @@ public class NioServerSocketChannelTest extends AbstractNioChannelTest<NioServer
@Test
public void testCloseOnError() throws Exception {
ServerSocketChannel jdkChannel = ServerSocketChannel.open();
NioServerSocketChannel serverSocketChannel = new NioServerSocketChannel(jdkChannel);
EventLoopGroup group = new NioEventLoopGroup(1);
NioServerSocketChannel serverSocketChannel = new NioServerSocketChannel(group.next(), group, jdkChannel);
try {
group.register(serverSocketChannel).syncUninterruptibly();
serverSocketChannel.register().syncUninterruptibly();
serverSocketChannel.bind(new InetSocketAddress(0)).syncUninterruptibly();
Assert.assertFalse(serverSocketChannel.closeOnReadError(new IOException()));
Assert.assertTrue(serverSocketChannel.closeOnReadError(new IllegalArgumentException()));
@ -46,8 +47,8 @@ public class NioServerSocketChannelTest extends AbstractNioChannelTest<NioServer
}
@Override
protected NioServerSocketChannel newNioChannel() {
return new NioServerSocketChannel();
protected NioServerSocketChannel newNioChannel(EventLoopGroup group) {
return new NioServerSocketChannel(group.next(), group);
}
@Override

View File

@ -161,15 +161,6 @@ public class NioSocketChannelTest extends AbstractNioChannelTest<NioSocketChanne
// Test for https://github.com/netty/netty/issues/4805
@Test(timeout = 3000)
public void testChannelReRegisterReadSameEventLoop() throws Exception {
testChannelReRegisterRead(true);
}
@Test(timeout = 3000)
public void testChannelReRegisterReadDifferentEventLoop() throws Exception {
testChannelReRegisterRead(false);
}
private static void testChannelReRegisterRead(final boolean sameEventLoop) throws Exception {
final EventLoopGroup group = new NioEventLoopGroup(2);
final CountDownLatch latch = new CountDownLatch(1);
@ -186,7 +177,7 @@ public class NioSocketChannelTest extends AbstractNioChannelTest<NioSocketChanne
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
protected void initChannel(final Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new SimpleChannelInboundHandler<ByteBuf>() {
@Override
@ -198,16 +189,7 @@ public class NioSocketChannelTest extends AbstractNioChannelTest<NioSocketChanne
@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
final EventLoop loop = group.next();
if (sameEventLoop) {
deregister(ctx, loop);
} else {
loop.execute(new Runnable() {
@Override
public void run() {
deregister(ctx, loop);
}
});
}
deregister(ctx, loop);
}
private void deregister(ChannelHandlerContext ctx, final EventLoop loop) {
@ -218,8 +200,7 @@ public class NioSocketChannelTest extends AbstractNioChannelTest<NioSocketChanne
@Override
public void operationComplete(ChannelFuture cf) {
Channel channel = cf.channel();
assertNotSame(loop, channel.eventLoop());
group.next().register(channel);
channel.register();
}
});
}
@ -282,8 +263,8 @@ public class NioSocketChannelTest extends AbstractNioChannelTest<NioSocketChanne
}
@Override
protected NioSocketChannel newNioChannel() {
return new NioSocketChannel();
protected NioSocketChannel newNioChannel(EventLoopGroup group) {
return new NioSocketChannel(group.next());
}
@Override