2012-02-18 23:02:56 +01:00
|
|
|
/*
|
2012-06-04 22:31:44 +02:00
|
|
|
* Copyright 2012 The Netty Project
|
2012-02-18 23:02:56 +01:00
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
|
|
|
*
|
2012-06-04 22:31:44 +02:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2012-02-18 23:02:56 +01:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-02-01 09:02:26 +01:00
|
|
|
package io.netty.channel.nio;
|
2012-02-18 23:02:56 +01:00
|
|
|
|
2014-08-05 14:24:49 +02:00
|
|
|
import io.netty.buffer.ByteBuf;
|
|
|
|
import io.netty.buffer.ByteBufAllocator;
|
|
|
|
import io.netty.buffer.ByteBufUtil;
|
|
|
|
import io.netty.buffer.Unpooled;
|
2012-02-18 23:02:56 +01:00
|
|
|
import io.netty.channel.AbstractChannel;
|
|
|
|
import io.netty.channel.Channel;
|
2012-05-27 07:48:48 +02:00
|
|
|
import io.netty.channel.ChannelException;
|
2013-06-11 11:46:39 +02:00
|
|
|
import io.netty.channel.ChannelFuture;
|
|
|
|
import io.netty.channel.ChannelFutureListener;
|
2012-12-30 17:40:24 +01:00
|
|
|
import io.netty.channel.ChannelPromise;
|
2013-03-07 20:53:45 +01:00
|
|
|
import io.netty.channel.ConnectTimeoutException;
|
2012-05-12 17:40:28 +02:00
|
|
|
import io.netty.channel.EventLoop;
|
2014-08-05 14:24:49 +02:00
|
|
|
import io.netty.util.ReferenceCountUtil;
|
|
|
|
import io.netty.util.ReferenceCounted;
|
2013-02-26 23:54:25 +01:00
|
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
2012-02-18 23:02:56 +01:00
|
|
|
|
2012-05-27 07:48:48 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.net.SocketAddress;
|
2013-01-16 12:20:35 +01:00
|
|
|
import java.nio.channels.CancelledKeyException;
|
2015-04-19 21:07:19 +02:00
|
|
|
import java.nio.channels.ClosedChannelException;
|
2016-08-18 16:58:17 +02:00
|
|
|
import java.nio.channels.ConnectionPendingException;
|
2012-05-02 08:01:58 +02:00
|
|
|
import java.nio.channels.SelectableChannel;
|
2012-05-02 14:05:53 +02:00
|
|
|
import java.nio.channels.SelectionKey;
|
2012-05-27 07:48:48 +02:00
|
|
|
import java.util.concurrent.ScheduledFuture;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
2012-02-18 23:02:56 +01:00
|
|
|
|
2012-12-21 17:59:37 +01:00
|
|
|
/**
|
|
|
|
* Abstract base class for {@link Channel} implementations which use a Selector based approach.
|
|
|
|
*/
|
2012-05-02 08:01:58 +02:00
|
|
|
public abstract class AbstractNioChannel extends AbstractChannel {
|
2012-02-18 23:02:56 +01:00
|
|
|
|
2012-05-27 07:48:48 +02:00
|
|
|
private static final InternalLogger logger =
|
|
|
|
InternalLoggerFactory.getInstance(AbstractNioChannel.class);
|
|
|
|
|
2012-05-02 14:05:53 +02:00
|
|
|
private final SelectableChannel ch;
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 13:53:59 +01:00
|
|
|
protected final int readInterestOp;
|
2014-06-27 17:09:27 +02:00
|
|
|
volatile SelectionKey selectionKey;
|
2016-04-05 19:13:18 +02:00
|
|
|
boolean readPending;
|
|
|
|
private final Runnable clearReadPendingRunnable = new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2016-04-08 04:49:02 +02:00
|
|
|
clearReadPending0();
|
2016-04-05 19:13:18 +02:00
|
|
|
}
|
|
|
|
};
|
2012-05-02 08:01:58 +02:00
|
|
|
|
2012-05-27 07:48:48 +02:00
|
|
|
/**
|
|
|
|
* The future of the current connection attempt. If not null, subsequent
|
|
|
|
* connection attempts will fail.
|
|
|
|
*/
|
2012-12-30 17:40:24 +01:00
|
|
|
private ChannelPromise connectPromise;
|
2012-05-27 07:48:48 +02:00
|
|
|
private ScheduledFuture<?> connectTimeoutFuture;
|
2013-02-22 22:45:17 +01:00
|
|
|
private SocketAddress requestedRemoteAddress;
|
2012-05-27 07:48:48 +02:00
|
|
|
|
2012-12-21 17:59:37 +01:00
|
|
|
/**
|
|
|
|
* Create a new instance
|
|
|
|
*
|
|
|
|
* @param parent the parent {@link Channel} by which this instance was created. May be {@code null}
|
|
|
|
* @param ch the underlying {@link SelectableChannel} on which it operates
|
|
|
|
* @param readInterestOp the ops to set to receive data from the {@link SelectableChannel}
|
|
|
|
*/
|
2013-07-09 07:49:06 +02:00
|
|
|
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
|
|
|
|
super(parent);
|
2012-05-02 08:01:58 +02:00
|
|
|
this.ch = ch;
|
2012-08-28 08:55:51 +02:00
|
|
|
this.readInterestOp = readInterestOp;
|
2012-05-27 07:48:48 +02:00
|
|
|
try {
|
|
|
|
ch.configureBlocking(false);
|
|
|
|
} catch (IOException e) {
|
|
|
|
try {
|
|
|
|
ch.close();
|
|
|
|
} catch (IOException e2) {
|
|
|
|
if (logger.isWarnEnabled()) {
|
|
|
|
logger.warn(
|
|
|
|
"Failed to close a partially initialized socket.", e2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new ChannelException("Failed to enter non-blocking mode.", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isOpen() {
|
|
|
|
return ch.isOpen();
|
2012-03-29 17:07:19 +02:00
|
|
|
}
|
2012-05-02 08:01:58 +02:00
|
|
|
|
|
|
|
@Override
|
2012-05-27 07:48:48 +02:00
|
|
|
public NioUnsafe unsafe() {
|
|
|
|
return (NioUnsafe) super.unsafe();
|
|
|
|
}
|
|
|
|
|
2012-05-02 08:01:58 +02:00
|
|
|
protected SelectableChannel javaChannel() {
|
|
|
|
return ch;
|
2012-03-06 19:26:32 +01:00
|
|
|
}
|
2012-05-02 08:01:58 +02:00
|
|
|
|
2012-10-24 18:27:26 +02:00
|
|
|
@Override
|
|
|
|
public NioEventLoop eventLoop() {
|
|
|
|
return (NioEventLoop) super.eventLoop();
|
|
|
|
}
|
|
|
|
|
2012-12-21 17:59:37 +01:00
|
|
|
/**
|
|
|
|
* Return the current {@link SelectionKey}
|
|
|
|
*/
|
2012-05-02 14:05:53 +02:00
|
|
|
protected SelectionKey selectionKey() {
|
|
|
|
assert selectionKey != null;
|
|
|
|
return selectionKey;
|
|
|
|
}
|
|
|
|
|
2016-04-05 19:13:18 +02:00
|
|
|
/**
|
|
|
|
* @deprecated No longer supported.
|
|
|
|
* No longer supported.
|
|
|
|
*/
|
|
|
|
@Deprecated
|
2014-04-10 08:27:27 +02:00
|
|
|
protected boolean isReadPending() {
|
|
|
|
return readPending;
|
|
|
|
}
|
|
|
|
|
2016-04-05 19:13:18 +02:00
|
|
|
/**
|
|
|
|
* @deprecated Use {@link #clearReadPending()} if appropriate instead.
|
|
|
|
* No longer supported.
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
protected void setReadPending(final boolean readPending) {
|
|
|
|
if (isRegistered()) {
|
|
|
|
EventLoop eventLoop = eventLoop();
|
|
|
|
if (eventLoop.inEventLoop()) {
|
2016-04-08 04:49:02 +02:00
|
|
|
setReadPending0(readPending);
|
2016-04-05 19:13:18 +02:00
|
|
|
} else {
|
2016-03-27 14:25:39 +02:00
|
|
|
eventLoop.execute(new Runnable() {
|
2016-04-05 19:13:18 +02:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2016-04-08 04:49:02 +02:00
|
|
|
setReadPending0(readPending);
|
2016-04-05 19:13:18 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
2016-04-08 18:44:42 +02:00
|
|
|
// Best effort if we are not registered yet clear readPending.
|
|
|
|
// NB: We only set the boolean field instead of calling clearReadPending0(), because the SelectionKey is
|
|
|
|
// not set yet so it would produce an assertion failure.
|
|
|
|
this.readPending = readPending;
|
2016-04-05 19:13:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set read pending to {@code false}.
|
|
|
|
*/
|
|
|
|
protected final void clearReadPending() {
|
|
|
|
if (isRegistered()) {
|
|
|
|
EventLoop eventLoop = eventLoop();
|
|
|
|
if (eventLoop.inEventLoop()) {
|
2016-04-08 04:49:02 +02:00
|
|
|
clearReadPending0();
|
2016-04-05 19:13:18 +02:00
|
|
|
} else {
|
|
|
|
eventLoop.execute(clearReadPendingRunnable);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Best effort if we are not registered yet clear readPending. This happens during channel initialization.
|
2016-04-08 10:23:32 +02:00
|
|
|
// NB: We only set the boolean field instead of calling clearReadPending0(), because the SelectionKey is
|
|
|
|
// not set yet so it would produce an assertion failure.
|
2016-04-08 10:44:52 +02:00
|
|
|
readPending = false;
|
2016-04-05 19:13:18 +02:00
|
|
|
}
|
2014-04-10 08:27:27 +02:00
|
|
|
}
|
|
|
|
|
2016-04-08 04:49:02 +02:00
|
|
|
private void setReadPending0(boolean readPending) {
|
|
|
|
this.readPending = readPending;
|
|
|
|
if (!readPending) {
|
|
|
|
((AbstractNioUnsafe) unsafe()).removeReadOp();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void clearReadPending0() {
|
|
|
|
readPending = false;
|
|
|
|
((AbstractNioUnsafe) unsafe()).removeReadOp();
|
|
|
|
}
|
|
|
|
|
2012-12-21 17:59:37 +01:00
|
|
|
/**
|
|
|
|
* Special {@link Unsafe} sub-type which allows to access the underlying {@link SelectableChannel}
|
|
|
|
*/
|
2012-05-27 07:48:48 +02:00
|
|
|
public interface NioUnsafe extends Unsafe {
|
2012-12-30 12:48:32 +01:00
|
|
|
/**
|
|
|
|
* Return underlying {@link SelectableChannel}
|
|
|
|
*/
|
2012-10-24 20:37:41 +02:00
|
|
|
SelectableChannel ch();
|
2012-12-30 12:48:32 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Finish connect
|
|
|
|
*/
|
2012-05-27 07:48:48 +02:00
|
|
|
void finishConnect();
|
2012-12-30 12:48:32 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Read from underlying {@link SelectableChannel}
|
|
|
|
*/
|
2012-05-27 07:48:48 +02:00
|
|
|
void read();
|
2013-07-12 18:45:24 +02:00
|
|
|
|
|
|
|
void forceFlush();
|
2012-05-27 07:48:48 +02:00
|
|
|
}
|
|
|
|
|
2012-05-27 14:07:23 +02:00
|
|
|
protected abstract class AbstractNioUnsafe extends AbstractUnsafe implements NioUnsafe {
|
2012-08-28 08:55:51 +02:00
|
|
|
|
2014-02-21 21:42:13 +01:00
|
|
|
protected final void removeReadOp() {
|
|
|
|
SelectionKey key = selectionKey();
|
|
|
|
// Check first if the key is still valid as it may be canceled as part of the deregistration
|
|
|
|
// from the EventLoop
|
|
|
|
// See https://github.com/netty/netty/issues/2104
|
|
|
|
if (!key.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int interestOps = key.interestOps();
|
|
|
|
if ((interestOps & readInterestOp) != 0) {
|
|
|
|
// only remove readInterestOp if needed
|
|
|
|
key.interestOps(interestOps & ~readInterestOp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2014-08-05 14:24:49 +02:00
|
|
|
public final SelectableChannel ch() {
|
2012-05-27 07:48:48 +02:00
|
|
|
return javaChannel();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2014-08-05 14:24:49 +02:00
|
|
|
public final void connect(
|
2012-12-30 17:40:24 +01:00
|
|
|
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
|
2014-02-10 23:52:24 +01:00
|
|
|
if (!promise.setUncancellable() || !ensureOpen(promise)) {
|
2013-05-17 12:20:46 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (connectPromise != null) {
|
2016-08-18 16:58:17 +02:00
|
|
|
// Already a connect in process.
|
|
|
|
throw new ConnectionPendingException();
|
2012-05-27 07:48:48 +02:00
|
|
|
}
|
|
|
|
|
2013-05-17 12:20:46 +02:00
|
|
|
boolean wasActive = isActive();
|
|
|
|
if (doConnect(remoteAddress, localAddress)) {
|
2013-11-18 09:00:23 +01:00
|
|
|
fulfillConnectPromise(promise, wasActive);
|
2013-05-17 12:20:46 +02:00
|
|
|
} else {
|
|
|
|
connectPromise = promise;
|
|
|
|
requestedRemoteAddress = remoteAddress;
|
|
|
|
|
|
|
|
// Schedule connect timeout.
|
|
|
|
int connectTimeoutMillis = config().getConnectTimeoutMillis();
|
|
|
|
if (connectTimeoutMillis > 0) {
|
2016-03-27 14:25:39 +02:00
|
|
|
connectTimeoutFuture = eventLoop().schedule(new Runnable() {
|
2013-05-17 12:20:46 +02:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
|
|
|
|
ConnectTimeoutException cause =
|
|
|
|
new ConnectTimeoutException("connection timed out: " + remoteAddress);
|
|
|
|
if (connectPromise != null && connectPromise.tryFailure(cause)) {
|
2013-05-15 15:10:41 +02:00
|
|
|
close(voidPromise());
|
2012-05-27 07:48:48 +02:00
|
|
|
}
|
2013-05-17 12:20:46 +02:00
|
|
|
}
|
|
|
|
}, connectTimeoutMillis, TimeUnit.MILLISECONDS);
|
2013-02-22 22:45:17 +01:00
|
|
|
}
|
2013-06-11 11:46:39 +02:00
|
|
|
|
|
|
|
promise.addListener(new ChannelFutureListener() {
|
|
|
|
@Override
|
|
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
|
|
if (future.isCancelled()) {
|
|
|
|
if (connectTimeoutFuture != null) {
|
|
|
|
connectTimeoutFuture.cancel(false);
|
|
|
|
}
|
|
|
|
connectPromise = null;
|
|
|
|
close(voidPromise());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2012-05-27 07:48:48 +02:00
|
|
|
}
|
2013-05-17 12:20:46 +02:00
|
|
|
} catch (Throwable t) {
|
2014-09-16 12:25:26 +02:00
|
|
|
promise.tryFailure(annotateConnectException(t, remoteAddress));
|
2013-10-08 05:24:16 +02:00
|
|
|
closeIfClosed();
|
2012-05-27 07:48:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-18 09:00:23 +01:00
|
|
|
private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
|
2014-01-09 11:24:44 +01:00
|
|
|
if (promise == null) {
|
|
|
|
// Closed via cancellation and the promise has been notified already.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-15 14:26:19 +02:00
|
|
|
// Get the state as trySuccess() may trigger an ChannelFutureListener that will close the Channel.
|
|
|
|
// We still need to ensure we call fireChannelActive() in this case.
|
|
|
|
boolean active = isActive();
|
|
|
|
|
2013-11-18 09:00:23 +01:00
|
|
|
// trySuccess() will return false if a user cancelled the connection attempt.
|
|
|
|
boolean promiseSet = promise.trySuccess();
|
|
|
|
|
|
|
|
// Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
|
|
|
|
// because what happened is what happened.
|
2016-08-15 14:26:19 +02:00
|
|
|
if (!wasActive && active) {
|
2013-11-18 09:00:23 +01:00
|
|
|
pipeline().fireChannelActive();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a user cancelled the connection attempt, close the channel, which is followed by channelInactive().
|
|
|
|
if (!promiseSet) {
|
|
|
|
close(voidPromise());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-09 11:24:44 +01:00
|
|
|
private void fulfillConnectPromise(ChannelPromise promise, Throwable cause) {
|
|
|
|
if (promise == null) {
|
|
|
|
// Closed via cancellation and the promise has been notified already.
|
2014-07-18 07:08:30 +02:00
|
|
|
return;
|
2014-01-09 11:24:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Use tryFailure() instead of setFailure() to avoid the race against cancel().
|
|
|
|
promise.tryFailure(cause);
|
|
|
|
closeIfClosed();
|
|
|
|
}
|
|
|
|
|
2012-05-27 07:48:48 +02:00
|
|
|
@Override
|
2014-08-05 14:24:49 +02:00
|
|
|
public final void finishConnect() {
|
2013-06-11 11:46:39 +02:00
|
|
|
// Note this method is invoked by the event loop only if the connection attempt was
|
|
|
|
// neither cancelled nor timed out.
|
|
|
|
|
2012-05-27 07:48:48 +02:00
|
|
|
assert eventLoop().inEventLoop();
|
2014-01-09 08:22:34 +01:00
|
|
|
|
2012-05-27 07:48:48 +02:00
|
|
|
try {
|
|
|
|
boolean wasActive = isActive();
|
|
|
|
doFinishConnect();
|
2014-01-09 11:24:44 +01:00
|
|
|
fulfillConnectPromise(connectPromise, wasActive);
|
2012-05-27 07:48:48 +02:00
|
|
|
} catch (Throwable t) {
|
2014-09-16 12:25:26 +02:00
|
|
|
fulfillConnectPromise(connectPromise, annotateConnectException(t, requestedRemoteAddress));
|
2012-05-27 07:48:48 +02:00
|
|
|
} finally {
|
2013-08-22 12:38:56 +02:00
|
|
|
// Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
|
|
|
|
// See https://github.com/netty/netty/issues/1770
|
|
|
|
if (connectTimeoutFuture != null) {
|
|
|
|
connectTimeoutFuture.cancel(false);
|
|
|
|
}
|
2012-12-30 17:40:24 +01:00
|
|
|
connectPromise = null;
|
2012-05-27 07:48:48 +02:00
|
|
|
}
|
|
|
|
}
|
2013-07-12 18:45:24 +02:00
|
|
|
|
|
|
|
@Override
|
2014-08-05 14:24:49 +02:00
|
|
|
protected final void flush0() {
|
2013-07-12 18:45:24 +02:00
|
|
|
// Flush immediately only when there's no pending flush.
|
|
|
|
// If there's a pending flush operation, event loop will call forceFlush() later,
|
|
|
|
// and thus there's no need to call it now.
|
2018-02-18 20:49:08 +01:00
|
|
|
if (!isFlushPending()) {
|
|
|
|
super.flush0();
|
2013-07-12 18:45:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2014-08-05 14:24:49 +02:00
|
|
|
public final void forceFlush() {
|
2013-07-12 18:45:24 +02:00
|
|
|
// directly call super.flush0() to force a flush now
|
|
|
|
super.flush0();
|
|
|
|
}
|
2013-08-09 13:30:04 +02:00
|
|
|
|
|
|
|
private boolean isFlushPending() {
|
|
|
|
SelectionKey selectionKey = selectionKey();
|
|
|
|
return selectionKey.isValid() && (selectionKey.interestOps() & SelectionKey.OP_WRITE) != 0;
|
|
|
|
}
|
2012-05-12 22:09:05 +02:00
|
|
|
}
|
2012-05-27 14:31:18 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean isCompatible(EventLoop loop) {
|
2012-08-10 13:17:18 +02:00
|
|
|
return loop instanceof NioEventLoop;
|
2012-05-27 14:31:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-07-27 20:08:45 +02:00
|
|
|
protected void doRegister() throws Exception {
|
2013-01-16 12:20:35 +01:00
|
|
|
boolean selected = false;
|
|
|
|
for (;;) {
|
|
|
|
try {
|
2016-11-23 22:50:42 +01:00
|
|
|
selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
|
2013-07-27 20:08:45 +02:00
|
|
|
return;
|
2013-01-16 12:20:35 +01:00
|
|
|
} catch (CancelledKeyException e) {
|
|
|
|
if (!selected) {
|
2013-07-28 22:05:34 +02:00
|
|
|
// Force the Selector to select now as the "canceled" SelectionKey may still be
|
2013-01-16 12:20:35 +01:00
|
|
|
// cached and not removed because no Select.select(..) operation was called yet.
|
|
|
|
eventLoop().selectNow();
|
|
|
|
selected = true;
|
|
|
|
} else {
|
|
|
|
// We forced a select operation on the selector before but the SelectionKey is still cached
|
|
|
|
// for whatever reason. JDK bug ?
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-27 14:31:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2013-07-26 18:13:56 +02:00
|
|
|
protected void doDeregister() throws Exception {
|
2012-12-21 17:59:37 +01:00
|
|
|
eventLoop().cancel(selectionKey());
|
2012-05-27 14:31:18 +02:00
|
|
|
}
|
|
|
|
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 13:53:59 +01:00
|
|
|
@Override
|
|
|
|
protected void doBeginRead() throws Exception {
|
2014-08-05 14:24:49 +02:00
|
|
|
// Channel.read() or ChannelHandlerContext.read() was called
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 13:53:59 +01:00
|
|
|
final SelectionKey selectionKey = this.selectionKey;
|
|
|
|
if (!selectionKey.isValid()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-05 14:24:49 +02:00
|
|
|
readPending = true;
|
|
|
|
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 13:53:59 +01:00
|
|
|
final int interestOps = selectionKey.interestOps();
|
2013-02-08 17:27:54 +01:00
|
|
|
if ((interestOps & readInterestOp) == 0) {
|
|
|
|
selectionKey.interestOps(interestOps | readInterestOp);
|
Read only when requested (read-on-demand)
This pull request introduces a new operation called read() that replaces the existing inbound traffic control method. EventLoop now performs socket reads only when the read() operation has been issued. Once the requested read() operation is actually performed, EventLoop triggers an inboundBufferSuspended event that tells the handlers that the requested read() operation has been performed and the inbound traffic has been suspended again. A handler can decide to continue reading or not.
Unlike other outbound operations, read() does not use ChannelFuture at all to avoid GC cost. If there's a good reason to create a new future per read at the GC cost, I'll change this.
This pull request consequently removes the readable property in ChannelHandlerContext, which means how the traffic control works changed significantly.
This pull request also adds a new configuration property ChannelOption.AUTO_READ whose default value is true. If true, Netty will call ctx.read() for you. If you need a close control over when read() is called, you can set it to false.
Another interesting fact is that non-terminal handlers do not really need to call read() at all. Only the last inbound handler will have to call it, and that's just enough. Actually, you don't even need to call it at the last handler in most cases because of the ChannelOption.AUTO_READ mentioned above.
There's no serious backward compatibility issue. If the compiler complains your handler does not implement the read() method, add the following:
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
Note that this pull request certainly makes bounded inbound buffer support very easy, but itself does not add the bounded inbound buffer support.
2012-12-30 13:53:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-21 17:59:37 +01:00
|
|
|
/**
|
2013-04-08 21:23:53 +02:00
|
|
|
* Connect to the remote peer
|
2012-12-21 17:59:37 +01:00
|
|
|
*/
|
2012-05-27 14:31:18 +02:00
|
|
|
protected abstract boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception;
|
2012-12-21 17:59:37 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Finish the connect
|
|
|
|
*/
|
2012-05-27 14:31:18 +02:00
|
|
|
protected abstract void doFinishConnect() throws Exception;
|
2014-08-05 14:24:49 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an off-heap copy of the specified {@link ByteBuf}, and releases the original one.
|
|
|
|
* Note that this method does not create an off-heap copy if the allocation / deallocation cost is too high,
|
|
|
|
* but just returns the original {@link ByteBuf}..
|
|
|
|
*/
|
|
|
|
protected final ByteBuf newDirectBuffer(ByteBuf buf) {
|
|
|
|
final int readableBytes = buf.readableBytes();
|
|
|
|
if (readableBytes == 0) {
|
|
|
|
ReferenceCountUtil.safeRelease(buf);
|
|
|
|
return Unpooled.EMPTY_BUFFER;
|
|
|
|
}
|
|
|
|
|
|
|
|
final ByteBufAllocator alloc = alloc();
|
|
|
|
if (alloc.isDirectBufferPooled()) {
|
|
|
|
ByteBuf directBuf = alloc.directBuffer(readableBytes);
|
|
|
|
directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
|
|
|
|
ReferenceCountUtil.safeRelease(buf);
|
|
|
|
return directBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
|
|
|
|
if (directBuf != null) {
|
|
|
|
directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
|
|
|
|
ReferenceCountUtil.safeRelease(buf);
|
|
|
|
return directBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocating and deallocating an unpooled direct buffer is very expensive; give up.
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns an off-heap copy of the specified {@link ByteBuf}, and releases the specified holder.
|
|
|
|
* The caller must ensure that the holder releases the original {@link ByteBuf} when the holder is released by
|
|
|
|
* this method. Note that this method does not create an off-heap copy if the allocation / deallocation cost is
|
|
|
|
* too high, but just returns the original {@link ByteBuf}..
|
|
|
|
*/
|
|
|
|
protected final ByteBuf newDirectBuffer(ReferenceCounted holder, ByteBuf buf) {
|
|
|
|
final int readableBytes = buf.readableBytes();
|
|
|
|
if (readableBytes == 0) {
|
|
|
|
ReferenceCountUtil.safeRelease(holder);
|
|
|
|
return Unpooled.EMPTY_BUFFER;
|
|
|
|
}
|
|
|
|
|
|
|
|
final ByteBufAllocator alloc = alloc();
|
|
|
|
if (alloc.isDirectBufferPooled()) {
|
|
|
|
ByteBuf directBuf = alloc.directBuffer(readableBytes);
|
|
|
|
directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
|
|
|
|
ReferenceCountUtil.safeRelease(holder);
|
|
|
|
return directBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
|
|
|
|
if (directBuf != null) {
|
|
|
|
directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
|
|
|
|
ReferenceCountUtil.safeRelease(holder);
|
|
|
|
return directBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocating and deallocating an unpooled direct buffer is very expensive; give up.
|
|
|
|
if (holder != buf) {
|
|
|
|
// Ensure to call holder.release() to give the holder a chance to release other resources than its content.
|
|
|
|
buf.retain();
|
|
|
|
ReferenceCountUtil.safeRelease(holder);
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
2015-04-19 21:07:19 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doClose() throws Exception {
|
|
|
|
ChannelPromise promise = connectPromise;
|
|
|
|
if (promise != null) {
|
|
|
|
// Use tryFailure() instead of setFailure() to avoid the race against cancel().
|
2019-05-17 22:23:02 +02:00
|
|
|
promise.tryFailure(new ClosedChannelException());
|
2015-04-19 21:07:19 +02:00
|
|
|
connectPromise = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScheduledFuture<?> future = connectTimeoutFuture;
|
|
|
|
if (future != null) {
|
|
|
|
future.cancel(false);
|
|
|
|
connectTimeoutFuture = null;
|
|
|
|
}
|
|
|
|
}
|
2012-02-18 23:02:56 +01:00
|
|
|
}
|