Retrofit/overhaul the NIO transport with the new API
- Remove large portion of code thanks to the new API - SocketChannel implementations are instantiated without factories - Retrofit the existing code with the new API
This commit is contained in:
parent
9e6f8b46df
commit
607d784e5e
@ -58,7 +58,9 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
private final ChannelFuture succeededFuture = new SucceededChannelFuture(this);
|
private final ChannelFuture succeededFuture = new SucceededChannelFuture(this);
|
||||||
|
|
||||||
private volatile EventLoop eventLoop;
|
private volatile EventLoop eventLoop;
|
||||||
|
private volatile boolean registered;
|
||||||
private volatile boolean notifiedClosureListeners;
|
private volatile boolean notifiedClosureListeners;
|
||||||
|
private ChannelFuture connectFuture;
|
||||||
|
|
||||||
/** Cache for the string representation of this channel */
|
/** Cache for the string representation of this channel */
|
||||||
private boolean strValActive;
|
private boolean strValActive;
|
||||||
@ -116,6 +118,11 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
return eventLoop;
|
return eventLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegistered() {
|
||||||
|
return registered;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(SocketAddress localAddress, ChannelFuture future) {
|
public void bind(SocketAddress localAddress, ChannelFuture future) {
|
||||||
pipeline().bind(localAddress, future);
|
pipeline().bind(localAddress, future);
|
||||||
@ -210,10 +217,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void notifyClosureListeners() {
|
||||||
* A {@link Channel} implementation must call this method when it is closed.
|
|
||||||
*/
|
|
||||||
protected void notifyClosureListeners() {
|
|
||||||
final ChannelFutureListener[] array;
|
final ChannelFutureListener[] array;
|
||||||
synchronized (closureListeners) {
|
synchronized (closureListeners) {
|
||||||
if (notifiedClosureListeners) {
|
if (notifiedClosureListeners) {
|
||||||
@ -382,6 +386,10 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
|
|
||||||
assert eventLoop().inEventLoop();
|
assert eventLoop().inEventLoop();
|
||||||
doRegister(future);
|
doRegister(future);
|
||||||
|
assert future.isDone();
|
||||||
|
if (registered = future.isSuccess()) {
|
||||||
|
pipeline().fireChannelRegistered();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -400,18 +408,32 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelFuture future) {
|
public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelFuture future) {
|
||||||
|
// XXX: What if a user makes a connection attempt twice?
|
||||||
if (eventLoop().inEventLoop()) {
|
if (eventLoop().inEventLoop()) {
|
||||||
doConnect(remoteAddress, localAddress, future);
|
doConnect(remoteAddress, localAddress, future);
|
||||||
|
if (!future.isDone()) {
|
||||||
|
connectFuture = future;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
eventLoop().execute(new Runnable() {
|
eventLoop().execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
doConnect(remoteAddress, localAddress, future);
|
doConnect(remoteAddress, localAddress, future);
|
||||||
|
if (!future.isDone()) {
|
||||||
|
connectFuture = future;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishConnect() {
|
||||||
|
assert eventLoop().inEventLoop();
|
||||||
|
assert connectFuture != null;
|
||||||
|
doFinishConnect(connectFuture);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect(final ChannelFuture future) {
|
public void disconnect(final ChannelFuture future) {
|
||||||
if (eventLoop().inEventLoop()) {
|
if (eventLoop().inEventLoop()) {
|
||||||
@ -430,11 +452,13 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
public void close(final ChannelFuture future) {
|
public void close(final ChannelFuture future) {
|
||||||
if (eventLoop().inEventLoop()) {
|
if (eventLoop().inEventLoop()) {
|
||||||
doClose(future);
|
doClose(future);
|
||||||
|
notifyClosureListeners();
|
||||||
} else {
|
} else {
|
||||||
eventLoop().execute(new Runnable() {
|
eventLoop().execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
doClose(future);
|
doClose(future);
|
||||||
|
notifyClosureListeners();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -446,6 +470,8 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
try {
|
try {
|
||||||
doDeregister(future);
|
doDeregister(future);
|
||||||
} finally {
|
} finally {
|
||||||
|
registered = false;
|
||||||
|
pipeline().fireChannelUnregistered();
|
||||||
eventLoop = null;
|
eventLoop = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -455,6 +481,8 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
try {
|
try {
|
||||||
doDeregister(future);
|
doDeregister(future);
|
||||||
} finally {
|
} finally {
|
||||||
|
registered = false;
|
||||||
|
pipeline().fireChannelUnregistered();
|
||||||
eventLoop = null;
|
eventLoop = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -493,6 +521,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
|||||||
protected abstract void doRegister(ChannelFuture future);
|
protected abstract void doRegister(ChannelFuture future);
|
||||||
protected abstract void doBind(SocketAddress localAddress, ChannelFuture future);
|
protected abstract void doBind(SocketAddress localAddress, ChannelFuture future);
|
||||||
protected abstract void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future);
|
protected abstract void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future);
|
||||||
|
protected abstract void doFinishConnect(ChannelFuture future);
|
||||||
protected abstract void doDisconnect(ChannelFuture future);
|
protected abstract void doDisconnect(ChannelFuture future);
|
||||||
protected abstract void doClose(ChannelFuture future);
|
protected abstract void doClose(ChannelFuture future);
|
||||||
protected abstract void doDeregister(ChannelFuture future);
|
protected abstract void doDeregister(ChannelFuture future);
|
||||||
|
@ -51,11 +51,26 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ChannelBufferHolder<Object> firstOut() {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SocketAddress remoteAddress0() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) {
|
protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) {
|
||||||
future.setFailure(new UnsupportedOperationException());
|
future.setFailure(new UnsupportedOperationException());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFinishConnect(ChannelFuture future) {
|
||||||
|
future.setFailure(new UnsupportedOperationException());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doDisconnect(ChannelFuture future) {
|
protected void doDisconnect(ChannelFuture future) {
|
||||||
future.setFailure(new UnsupportedOperationException());
|
future.setFailure(new UnsupportedOperationException());
|
||||||
|
@ -175,6 +175,8 @@ public interface Channel extends AttributeMap, ChannelFutureFactory, Comparable<
|
|||||||
void flush(ChannelFuture future);
|
void flush(ChannelFuture future);
|
||||||
void write(Object message, ChannelFuture future);
|
void write(Object message, ChannelFuture future);
|
||||||
|
|
||||||
|
// FIXME: Introduce more flexible channel state notification mechanism
|
||||||
|
// - notify me when channel becomes (un)registered, (in)active
|
||||||
void addClosureListener(ChannelFutureListener listener);
|
void addClosureListener(ChannelFutureListener listener);
|
||||||
void removeClosureListener(ChannelFutureListener remover);
|
void removeClosureListener(ChannelFutureListener remover);
|
||||||
|
|
||||||
@ -190,6 +192,7 @@ public interface Channel extends AttributeMap, ChannelFutureFactory, Comparable<
|
|||||||
void register(EventLoop eventLoop, ChannelFuture future);
|
void register(EventLoop eventLoop, ChannelFuture future);
|
||||||
void bind(SocketAddress localAddress, ChannelFuture future);
|
void bind(SocketAddress localAddress, ChannelFuture future);
|
||||||
void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future);
|
void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future);
|
||||||
|
void finishConnect();
|
||||||
void disconnect(ChannelFuture future);
|
void disconnect(ChannelFuture future);
|
||||||
void close(ChannelFuture future);
|
void close(ChannelFuture future);
|
||||||
void deregister(ChannelFuture future);
|
void deregister(ChannelFuture future);
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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 java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import io.netty.channel.group.ChannelGroup;
|
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
|
||||||
import io.netty.util.ExternalResourceReleasable;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main interface to a transport that creates a {@link Channel} associated
|
|
||||||
* with a certain communication entity such as a network socket. For example,
|
|
||||||
* the {@link NioServerSocketChannelFactory} creates a channel which has a
|
|
||||||
* NIO-based server socket as its underlying communication entity.
|
|
||||||
* <p>
|
|
||||||
* Once a new {@link Channel} is created, the {@link ChannelPipeline} which
|
|
||||||
* was specified as a parameter in the {@link #newChannel(ChannelPipeline)}
|
|
||||||
* is attached to the new {@link Channel}, and starts to handle all associated
|
|
||||||
* {@link ChannelEvent}s.
|
|
||||||
*
|
|
||||||
* <h3>Graceful shutdown</h3>
|
|
||||||
* <p>
|
|
||||||
* To shut down a network application service which is managed by a factory.
|
|
||||||
* you should follow the following steps:
|
|
||||||
* <ol>
|
|
||||||
* <li>close all channels created by the factory and their child channels
|
|
||||||
* usually using {@link ChannelGroup#close()}, and</li>
|
|
||||||
* <li>call {@link #releaseExternalResources()}.</li>
|
|
||||||
* </ol>
|
|
||||||
* <p>
|
|
||||||
* For detailed transport-specific information on shutting down a factory,
|
|
||||||
* please refer to the Javadoc of {@link ChannelFactory}'s subtypes, such as
|
|
||||||
* {@link NioServerSocketChannelFactory}.
|
|
||||||
* @apiviz.landmark
|
|
||||||
* @apiviz.has io.netty.channel.Channel oneway - - creates
|
|
||||||
*
|
|
||||||
* @apiviz.exclude ^io\.netty\.channel\.([a-z]+\.)+.*ChannelFactory$
|
|
||||||
*/
|
|
||||||
public interface ChannelFactory extends ExternalResourceReleasable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and opens a new {@link Channel} and attaches the specified
|
|
||||||
* {@link ChannelPipeline} to the new {@link Channel}.
|
|
||||||
*
|
|
||||||
* @param pipeline the {@link ChannelPipeline} which is going to be
|
|
||||||
* attached to the new {@link Channel}
|
|
||||||
*
|
|
||||||
* @return the newly open channel
|
|
||||||
*
|
|
||||||
* @throws ChannelException if failed to create and open a new channel
|
|
||||||
*/
|
|
||||||
Channel newChannel(ChannelPipeline pipeline);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the external resources that this factory depends on to function.
|
|
||||||
* An external resource is a resource that this factory didn't create by
|
|
||||||
* itself. For example, {@link Executor}s that you specified in the factory
|
|
||||||
* constructor are external resources. You can call this method to release
|
|
||||||
* all external resources conveniently when the resources are not used by
|
|
||||||
* this factory or any other part of your application. An unexpected
|
|
||||||
* behavior will be resulted in if the resources are released when there's
|
|
||||||
* an open channel which is managed by this factory.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
void releaseExternalResources();
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ChannelFactory} that creates a {@link ServerChannel}.
|
|
||||||
* @apiviz.has io.netty.channel.ServerChannel oneway - - creates
|
|
||||||
*/
|
|
||||||
public interface ServerChannelFactory extends ChannelFactory {
|
|
||||||
@Override
|
|
||||||
ServerChannel newChannel(ChannelPipeline pipeline);
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ChannelFactory} which creates a client-side {@link SocketChannel}.
|
|
||||||
* @apiviz.has io.netty.channel.socket.SocketChannel oneway - - creates
|
|
||||||
*/
|
|
||||||
public interface ClientSocketChannelFactory extends ChannelFactory {
|
|
||||||
@Override
|
|
||||||
SocketChannel newChannel(ChannelPipeline pipeline);
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ChannelFactory} which creates a {@link DatagramChannel}.
|
|
||||||
* @apiviz.has io.netty.channel.socket.DatagramChannel oneway - - creates
|
|
||||||
*/
|
|
||||||
public interface DatagramChannelFactory extends ChannelFactory {
|
|
||||||
@Override
|
|
||||||
DatagramChannel newChannel(ChannelPipeline pipeline);
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ServerChannelFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ChannelFactory} which creates a {@link ServerSocketChannel}.
|
|
||||||
* @apiviz.has io.netty.channel.socket.ServerSocketChannel oneway - - creates
|
|
||||||
*/
|
|
||||||
public interface ServerSocketChannelFactory extends ServerChannelFactory {
|
|
||||||
@Override
|
|
||||||
ServerSocketChannel newChannel(ChannelPipeline pipeline);
|
|
||||||
}
|
|
@ -17,41 +17,21 @@ package io.netty.channel.socket.nio;
|
|||||||
|
|
||||||
import io.netty.channel.AbstractChannel;
|
import io.netty.channel.AbstractChannel;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.channels.SelectableChannel;
|
import java.nio.channels.SelectableChannel;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.nio.channels.SelectionKey;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
public abstract class AbstractNioChannel extends AbstractChannel {
|
public abstract class AbstractNioChannel extends AbstractChannel {
|
||||||
|
|
||||||
/**
|
private final SelectableChannel ch;
|
||||||
* Indicates if there is a {@link WriteTask} in the task queue.
|
|
||||||
*/
|
|
||||||
final AtomicBoolean writeTaskInTaskQueue = new AtomicBoolean();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps track of the number of bytes that the {@link WriteRequestQueue} currently
|
|
||||||
* contains.
|
|
||||||
*/
|
|
||||||
final AtomicInteger writeBufferSize = new AtomicInteger();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps track of the highWaterMark.
|
|
||||||
*/
|
|
||||||
final AtomicInteger highWaterMarkCounter = new AtomicInteger();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boolean that indicates that write operation is in progress.
|
|
||||||
*/
|
|
||||||
protected boolean inWriteNowLoop;
|
|
||||||
protected boolean writeSuspended;
|
|
||||||
|
|
||||||
|
|
||||||
private volatile InetSocketAddress localAddress;
|
private volatile InetSocketAddress localAddress;
|
||||||
volatile InetSocketAddress remoteAddress;
|
private volatile InetSocketAddress remoteAddress;
|
||||||
|
|
||||||
private final SelectableChannel ch;
|
private volatile SelectionKey selectionKey;
|
||||||
|
|
||||||
protected AbstractNioChannel(Integer id, Channel parent, SelectableChannel ch) {
|
protected AbstractNioChannel(Integer id, Channel parent, SelectableChannel ch) {
|
||||||
super(id, parent);
|
super(id, parent);
|
||||||
@ -68,6 +48,11 @@ public abstract class AbstractNioChannel extends AbstractChannel {
|
|||||||
return ch;
|
return ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected SelectionKey selectionKey() {
|
||||||
|
assert selectionKey != null;
|
||||||
|
return selectionKey;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress localAddress() {
|
public InetSocketAddress localAddress() {
|
||||||
InetSocketAddress localAddress = this.localAddress;
|
InetSocketAddress localAddress = this.localAddress;
|
||||||
@ -100,4 +85,18 @@ public abstract class AbstractNioChannel extends AbstractChannel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract NioChannelConfig config();
|
public abstract NioChannelConfig config();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doRegister(ChannelFuture future) {
|
||||||
|
if (!(eventLoop() instanceof SelectorEventLoop)) {
|
||||||
|
throw new ChannelException("unsupported event loop: " + eventLoop().getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectorEventLoop loop = (SelectorEventLoop) eventLoop();
|
||||||
|
try {
|
||||||
|
selectionKey = javaChannel().register(loop.selector, javaChannel().validOps() & ~SelectionKey.OP_WRITE, this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ChannelException("failed to register a channel", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
|
|
||||||
final class NioAcceptedSocketChannel extends NioSocketChannel {
|
|
||||||
|
|
||||||
|
|
||||||
static NioAcceptedSocketChannel create(ChannelFactory factory,
|
|
||||||
ChannelPipeline pipeline, Channel parent, ChannelSink sink,
|
|
||||||
SocketChannel socket, NioWorker worker) {
|
|
||||||
NioAcceptedSocketChannel instance = new NioAcceptedSocketChannel(
|
|
||||||
factory, pipeline, parent, sink, socket, worker);
|
|
||||||
instance.setConnected();
|
|
||||||
fireChannelOpen(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NioAcceptedSocketChannel(
|
|
||||||
ChannelFactory factory, ChannelPipeline pipeline,
|
|
||||||
Channel parent, ChannelSink sink,
|
|
||||||
SocketChannel socket, NioWorker worker) {
|
|
||||||
|
|
||||||
super(parent, factory, pipeline, sink, socket, worker);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelException;
|
|
||||||
import io.netty.channel.ChannelFactory;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.logging.InternalLogger;
|
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
final class NioClientSocketChannel extends NioSocketChannel {
|
|
||||||
|
|
||||||
private static final InternalLogger logger =
|
|
||||||
InternalLoggerFactory.getInstance(NioClientSocketChannel.class);
|
|
||||||
|
|
||||||
private static SocketChannel newSocket() {
|
|
||||||
SocketChannel socket;
|
|
||||||
try {
|
|
||||||
socket = SocketChannel.open();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ChannelException("Failed to open a socket.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
|
||||||
socket.configureBlocking(false);
|
|
||||||
success = true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ChannelException("Failed to enter non-blocking mode.", e);
|
|
||||||
} finally {
|
|
||||||
if (!success) {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn(
|
|
||||||
"Failed to close a partially initialized socket.",
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
volatile ChannelFuture connectFuture;
|
|
||||||
volatile boolean boundManually;
|
|
||||||
|
|
||||||
// Does not need to be volatile as it's accessed by only one thread.
|
|
||||||
long connectDeadlineNanos;
|
|
||||||
|
|
||||||
static NioClientSocketChannel create(ChannelFactory factory,
|
|
||||||
ChannelPipeline pipeline, ChannelSink sink, NioWorker worker) {
|
|
||||||
NioClientSocketChannel instance =
|
|
||||||
new NioClientSocketChannel(factory, pipeline, sink, worker);
|
|
||||||
fireChannelOpen(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NioClientSocketChannel(
|
|
||||||
ChannelFactory factory, ChannelPipeline pipeline,
|
|
||||||
ChannelSink sink, NioWorker worker) {
|
|
||||||
|
|
||||||
super(null, factory, pipeline, sink, newSocket(), worker);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.group.ChannelGroup;
|
|
||||||
import io.netty.channel.socket.ClientSocketChannelFactory;
|
|
||||||
import io.netty.channel.socket.SocketChannel;
|
|
||||||
import io.netty.util.ExternalResourceReleasable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ClientSocketChannelFactory} which creates a client-side NIO-based
|
|
||||||
* {@link SocketChannel}. It utilizes the non-blocking I/O mode which was
|
|
||||||
* introduced with NIO to serve many number of concurrent connections
|
|
||||||
* efficiently.
|
|
||||||
*
|
|
||||||
* <h3>How threads work</h3>
|
|
||||||
* <p>
|
|
||||||
* There are two types of threads in a {@link NioClientSocketChannelFactory};
|
|
||||||
* one is boss thread and the other is worker thread.
|
|
||||||
*
|
|
||||||
* <h4>Boss thread</h4>
|
|
||||||
* <p>
|
|
||||||
* One {@link NioClientSocketChannelFactory} has one boss thread. It makes
|
|
||||||
* a connection attempt on request. Once a connection attempt succeeds,
|
|
||||||
* the boss thread passes the connected {@link Channel} to one of the worker
|
|
||||||
* threads that the {@link NioClientSocketChannelFactory} manages.
|
|
||||||
*
|
|
||||||
* <h4>Worker threads</h4>
|
|
||||||
* <p>
|
|
||||||
* One {@link NioClientSocketChannelFactory} can have one or more worker
|
|
||||||
* threads. A worker thread performs non-blocking read and write for one or
|
|
||||||
* more {@link Channel}s in a non-blocking mode.
|
|
||||||
*
|
|
||||||
* <h3>Life cycle of threads and graceful shutdown</h3>
|
|
||||||
* <p>
|
|
||||||
* All threads are acquired from the {@link Executor}s which were specified
|
|
||||||
* when a {@link NioClientSocketChannelFactory} was created. A boss thread is
|
|
||||||
* acquired from the {@code bossExecutor}, and worker threads are acquired from
|
|
||||||
* the {@code workerExecutor}. Therefore, you should make sure the specified
|
|
||||||
* {@link Executor}s are able to lend the sufficient number of threads.
|
|
||||||
* It is the best bet to specify {@linkplain Executors#newCachedThreadPool() a cached thread pool}.
|
|
||||||
* <p>
|
|
||||||
* Both boss and worker threads are acquired lazily, and then released when
|
|
||||||
* there's nothing left to process. All the related resources such as
|
|
||||||
* {@link Selector} are also released when the boss and worker threads are
|
|
||||||
* released. Therefore, to shut down a service gracefully, you should do the
|
|
||||||
* following:
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>close all channels created by the factory usually using
|
|
||||||
* {@link ChannelGroup#close()}, and</li>
|
|
||||||
* <li>call {@link #releaseExternalResources()}.</li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* Please make sure not to shut down the executor until all channels are
|
|
||||||
* closed. Otherwise, you will end up with a {@link RejectedExecutionException}
|
|
||||||
* and the related resources might not be released properly.
|
|
||||||
* @apiviz.landmark
|
|
||||||
*/
|
|
||||||
public class NioClientSocketChannelFactory implements ClientSocketChannelFactory {
|
|
||||||
|
|
||||||
|
|
||||||
private final WorkerPool<NioWorker> workerPool;
|
|
||||||
private final ChannelSink sink;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link NioClientSocketChannelFactory} which uses {@link Executors#newCachedThreadPool()} for the worker executor.
|
|
||||||
*
|
|
||||||
* See {@link #NioClientSocketChannelFactory(Executor, Executor)}
|
|
||||||
*/
|
|
||||||
public NioClientSocketChannelFactory() {
|
|
||||||
this(Executors.newCachedThreadPool());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance. Calling this constructor is same with calling
|
|
||||||
* {@link #NioClientSocketChannelFactory(Executor, Executor, int, int)} with
|
|
||||||
* 1 and (2 * the number of available processors in the machine) for
|
|
||||||
* <tt>bossCount</tt> and <tt>workerCount</tt> respectively. The number of
|
|
||||||
* available processors is obtained by {@link Runtime#availableProcessors()}.
|
|
||||||
*
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the worker threads
|
|
||||||
*/
|
|
||||||
public NioClientSocketChannelFactory(Executor workerExecutor) {
|
|
||||||
this(workerExecutor, SelectorUtil.DEFAULT_IO_THREADS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance. Calling this constructor is same with calling
|
|
||||||
* {@link #NioClientSocketChannelFactory(Executor, int, int)} with
|
|
||||||
* 1 as <tt>bossCount</tt>.
|
|
||||||
*
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the worker threads
|
|
||||||
* @param workerCount
|
|
||||||
* the maximum number of worker threads
|
|
||||||
*/
|
|
||||||
public NioClientSocketChannelFactory(Executor workerExecutor,
|
|
||||||
int workerCount) {
|
|
||||||
this(new NioWorkerPool(workerExecutor, workerCount, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
public NioClientSocketChannelFactory(WorkerPool<NioWorker> workerPool) {
|
|
||||||
|
|
||||||
if (workerPool == null) {
|
|
||||||
throw new NullPointerException("workerPool");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.workerPool = workerPool;
|
|
||||||
sink = new NioClientSocketPipelineSink();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SocketChannel newChannel(ChannelPipeline pipeline) {
|
|
||||||
return NioClientSocketChannel.create(this, pipeline, sink, workerPool.nextWorker());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void releaseExternalResources() {
|
|
||||||
if (workerPool instanceof ExternalResourceReleasable) {
|
|
||||||
((ExternalResourceReleasable) workerPool).releaseExternalResources();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.fireChannelBound;
|
|
||||||
import static io.netty.channel.Channels.fireExceptionCaught;
|
|
||||||
import static io.netty.channel.Channels.succeededFuture;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelState;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.logging.InternalLogger;
|
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
|
|
||||||
class NioClientSocketPipelineSink extends AbstractNioChannelSink {
|
|
||||||
|
|
||||||
static final InternalLogger logger =
|
|
||||||
InternalLoggerFactory.getInstance(NioClientSocketPipelineSink.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventSunk(
|
|
||||||
ChannelPipeline pipeline, ChannelEvent e) throws Exception {
|
|
||||||
if (e instanceof ChannelStateEvent) {
|
|
||||||
ChannelStateEvent event = (ChannelStateEvent) e;
|
|
||||||
NioClientSocketChannel channel =
|
|
||||||
(NioClientSocketChannel) event.channel();
|
|
||||||
ChannelFuture future = event.getFuture();
|
|
||||||
ChannelState state = event.getState();
|
|
||||||
Object value = event.getValue();
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case OPEN:
|
|
||||||
if (Boolean.FALSE.equals(value)) {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BOUND:
|
|
||||||
if (value != null) {
|
|
||||||
bind(channel, future, (SocketAddress) value);
|
|
||||||
} else {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CONNECTED:
|
|
||||||
if (value != null) {
|
|
||||||
connect(channel, future, (SocketAddress) value);
|
|
||||||
} else {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INTEREST_OPS:
|
|
||||||
channel.getWorker().setInterestOps(channel, future, ((Integer) value).intValue());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (e instanceof MessageEvent) {
|
|
||||||
MessageEvent event = (MessageEvent) e;
|
|
||||||
NioSocketChannel channel = (NioSocketChannel) event.channel();
|
|
||||||
boolean offered = channel.writeBufferQueue.offer(event);
|
|
||||||
assert offered;
|
|
||||||
channel.getWorker().writeFromUserCode(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(
|
|
||||||
NioClientSocketChannel channel, ChannelFuture future,
|
|
||||||
SocketAddress localAddress) {
|
|
||||||
try {
|
|
||||||
channel.getJdkChannel().bind(localAddress);
|
|
||||||
channel.boundManually = true;
|
|
||||||
channel.setBound();
|
|
||||||
future.setSuccess();
|
|
||||||
fireChannelBound(channel, channel.getLocalAddress());
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void connect(
|
|
||||||
final NioClientSocketChannel channel, final ChannelFuture cf,
|
|
||||||
SocketAddress remoteAddress) {
|
|
||||||
try {
|
|
||||||
channel.getJdkChannel().connect(remoteAddress);
|
|
||||||
channel.getCloseFuture().addListener(new ChannelFutureListener() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(ChannelFuture f)
|
|
||||||
throws Exception {
|
|
||||||
if (!cf.isDone()) {
|
|
||||||
cf.setFailure(new ClosedChannelException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cf.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
|
||||||
channel.connectFuture = cf;
|
|
||||||
|
|
||||||
channel.getWorker().registerWithWorker(channel, cf);
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
cf.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
channel.getWorker().close(channel, succeededFuture(channel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,194 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.group.ChannelGroup;
|
|
||||||
import io.netty.channel.socket.DatagramChannel;
|
|
||||||
import io.netty.channel.socket.DatagramChannelFactory;
|
|
||||||
import io.netty.channel.socket.Worker;
|
|
||||||
import io.netty.channel.socket.nio.NioDatagramChannel.ProtocolFamily;
|
|
||||||
import io.netty.channel.socket.oio.OioDatagramChannelFactory;
|
|
||||||
import io.netty.util.ExternalResourceReleasable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link DatagramChannelFactory} that creates a NIO-based connectionless
|
|
||||||
* {@link DatagramChannel}. It utilizes the non-blocking I/O mode which
|
|
||||||
* was introduced with NIO to serve many number of concurrent connections
|
|
||||||
* efficiently.
|
|
||||||
*
|
|
||||||
* <h3>How threads work</h3>
|
|
||||||
* <p>
|
|
||||||
* There is only one thread type in a {@link NioDatagramChannelFactory};
|
|
||||||
* worker threads.
|
|
||||||
*
|
|
||||||
* <h4>Worker threads</h4>
|
|
||||||
* <p>
|
|
||||||
* One {@link NioDatagramChannelFactory} can have one or more worker
|
|
||||||
* threads. A worker thread performs non-blocking read and write for one or
|
|
||||||
* more {@link DatagramChannel}s in a non-blocking mode.
|
|
||||||
*
|
|
||||||
* <h3>Life cycle of threads and graceful shutdown</h3>
|
|
||||||
* <p>
|
|
||||||
* All worker threads are acquired from the {@link Executor} which was specified
|
|
||||||
* when a {@link NioDatagramChannelFactory} was created. Therefore, you should
|
|
||||||
* make sure the specified {@link Executor} is able to lend the sufficient
|
|
||||||
* number of threads. It is the best bet to specify
|
|
||||||
* {@linkplain Executors#newCachedThreadPool() a cached thread pool}.
|
|
||||||
* <p>
|
|
||||||
* All worker threads are acquired lazily, and then released when there's
|
|
||||||
* nothing left to process. All the related resources such as {@link Selector}
|
|
||||||
* are also released when the worker threads are released. Therefore, to shut
|
|
||||||
* down a service gracefully, you should do the following:
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>close all channels created by the factory usually using
|
|
||||||
* {@link ChannelGroup#close()}, and</li>
|
|
||||||
* <li>call {@link #releaseExternalResources()}.</li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* Please make sure not to shut down the executor until all channels are
|
|
||||||
* closed. Otherwise, you will end up with a {@link RejectedExecutionException}
|
|
||||||
* and the related resources might not be released properly.
|
|
||||||
*
|
|
||||||
* <h3>Limitation</h3>
|
|
||||||
* <p>
|
|
||||||
* Multicast is not supported. Please use {@link OioDatagramChannelFactory}
|
|
||||||
* instead.
|
|
||||||
* @apiviz.landmark
|
|
||||||
*/
|
|
||||||
public class NioDatagramChannelFactory implements DatagramChannelFactory {
|
|
||||||
|
|
||||||
private final ChannelSink sink;
|
|
||||||
private final WorkerPool<NioDatagramWorker> workerPool;
|
|
||||||
private final NioDatagramChannel.ProtocolFamily family;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link NioDatagramChannelFactory} with a {@link Executors#newCachedThreadPool()}.
|
|
||||||
*
|
|
||||||
* See {@link #NioDatagramChannelFactory(Executor)}
|
|
||||||
*/
|
|
||||||
public NioDatagramChannelFactory() {
|
|
||||||
this(Executors.newCachedThreadPool());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance. Calling this constructor is same with calling
|
|
||||||
* {@link #NioDatagramChannelFactory(Executor, int)} with 2 * the number of
|
|
||||||
* available processors in the machine. The number of available processors
|
|
||||||
* is obtained by {@link Runtime#availableProcessors()}.
|
|
||||||
*
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads
|
|
||||||
*/
|
|
||||||
public NioDatagramChannelFactory(final Executor workerExecutor) {
|
|
||||||
this(workerExecutor, SelectorUtil.DEFAULT_IO_THREADS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads
|
|
||||||
* @param workerCount
|
|
||||||
* the maximum number of I/O worker threads
|
|
||||||
*/
|
|
||||||
public NioDatagramChannelFactory(final Executor workerExecutor,
|
|
||||||
final int workerCount) {
|
|
||||||
this(new NioDatagramWorkerPool(workerExecutor, workerCount, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param workerPool
|
|
||||||
* the {@link WorkerPool} which will be used to obtain the {@link Worker} that execute the I/O worker threads
|
|
||||||
*/
|
|
||||||
public NioDatagramChannelFactory(WorkerPool<NioDatagramWorker> workerPool) {
|
|
||||||
this(workerPool, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance. Calling this constructor is same with calling
|
|
||||||
* {@link #NioDatagramChannelFactory(Executor, int)} with 2 * the number of
|
|
||||||
* available processors in the machine. The number of available processors
|
|
||||||
* is obtained by {@link Runtime#availableProcessors()}.
|
|
||||||
*
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads
|
|
||||||
* @param family
|
|
||||||
* the {@link ProtocolFamily} to use. This should be used for UDP multicast.
|
|
||||||
* <strong>Be aware that this option is only considered when running on java7+</strong>
|
|
||||||
*/
|
|
||||||
public NioDatagramChannelFactory(final Executor workerExecutor, ProtocolFamily family) {
|
|
||||||
this(workerExecutor, SelectorUtil.DEFAULT_IO_THREADS, family);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads
|
|
||||||
* @param workerCount
|
|
||||||
* the maximum number of I/O worker threads
|
|
||||||
* @param family
|
|
||||||
* the {@link ProtocolFamily} to use. This should be used for UDP multicast.
|
|
||||||
* <strong>Be aware that this option is only considered when running on java7+</strong>
|
|
||||||
*/
|
|
||||||
public NioDatagramChannelFactory(final Executor workerExecutor,
|
|
||||||
final int workerCount, ProtocolFamily family) {
|
|
||||||
this(new NioDatagramWorkerPool(workerExecutor, workerCount, true), family);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param workerPool
|
|
||||||
* the {@link WorkerPool} which will be used to obtain the {@link Worker} that execute the I/O worker threads
|
|
||||||
* @param family
|
|
||||||
* the {@link ProtocolFamily} to use. This should be used for UDP multicast.
|
|
||||||
* <strong>Be aware that this option is only considered when running on java7+</strong>
|
|
||||||
*/
|
|
||||||
public NioDatagramChannelFactory(WorkerPool<NioDatagramWorker> workerPool, ProtocolFamily family) {
|
|
||||||
this.workerPool = workerPool;
|
|
||||||
this.family = family;
|
|
||||||
sink = new NioDatagramPipelineSink();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DatagramChannel newChannel(final ChannelPipeline pipeline) {
|
|
||||||
return NioDatagramChannel.create(this, pipeline, sink, workerPool.nextWorker(), family);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void releaseExternalResources() {
|
|
||||||
if (workerPool instanceof ExternalResourceReleasable) {
|
|
||||||
((ExternalResourceReleasable) workerPool).releaseExternalResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.DatagramChannel;
|
|
||||||
|
|
||||||
class NioDatagramJdkChannel extends AbstractJdkChannel {
|
|
||||||
|
|
||||||
NioDatagramJdkChannel(DatagramChannel channel) {
|
|
||||||
super(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected DatagramChannel getChannel() {
|
|
||||||
return (DatagramChannel) super.getChannel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress getRemoteSocketAddress() {
|
|
||||||
return (InetSocketAddress) getChannel().socket().getRemoteSocketAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress getLocalSocketAddress() {
|
|
||||||
return (InetSocketAddress) getChannel().socket().getLocalSocketAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSocketBound() {
|
|
||||||
return getChannel().socket().isBound();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bind(SocketAddress local) throws IOException {
|
|
||||||
getChannel().socket().bind(local);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connect(SocketAddress remote) throws IOException {
|
|
||||||
getChannel().connect(remote);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected() {
|
|
||||||
return getChannel().isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnectSocket() throws IOException {
|
|
||||||
getChannel().disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeSocket() throws IOException {
|
|
||||||
getChannel().socket().close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int write(ByteBuffer src) throws IOException {
|
|
||||||
return getChannel().write(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default implementation which hands of {@link NioDatagramWorker}'s
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class NioDatagramWorkerPool extends AbstractNioWorkerPool<NioDatagramWorker> {
|
|
||||||
|
|
||||||
public NioDatagramWorkerPool(Executor executor, int workerCount, boolean allowShutdownOnIdle) {
|
|
||||||
super(executor, workerCount, allowShutdownOnIdle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected NioDatagramWorker createWorker(Executor executor, boolean allowShutdownOnIdle) {
|
|
||||||
return new NioDatagramWorker(executor, allowShutdownOnIdle);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,447 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.channels.SelectionKey;
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
import java.nio.channels.ServerSocketChannel;
|
|
||||||
import java.nio.channels.spi.SelectorProvider;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import io.netty.logging.InternalLogger;
|
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
|
||||||
import io.netty.util.internal.SystemPropertyUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides information which is specific to a NIO service provider
|
|
||||||
* implementation.
|
|
||||||
*/
|
|
||||||
final class NioProviderMetadata {
|
|
||||||
static final InternalLogger logger =
|
|
||||||
InternalLoggerFactory.getInstance(NioProviderMetadata.class);
|
|
||||||
|
|
||||||
private static final String CONSTRAINT_LEVEL_PROPERTY =
|
|
||||||
"io.netty.channel.socket.nio.constraintLevel";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 0 - no need to wake up to get / set interestOps (most cases)
|
|
||||||
* 1 - no need to wake up to get interestOps, but need to wake up to set.
|
|
||||||
* 2 - need to wake up to get / set interestOps (old providers)
|
|
||||||
*/
|
|
||||||
static final int CONSTRAINT_LEVEL;
|
|
||||||
|
|
||||||
static {
|
|
||||||
int constraintLevel = -1;
|
|
||||||
|
|
||||||
// Use the system property if possible.
|
|
||||||
constraintLevel = SystemPropertyUtil.get(CONSTRAINT_LEVEL_PROPERTY, -1);
|
|
||||||
if (constraintLevel < 0 || constraintLevel > 2) {
|
|
||||||
constraintLevel = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (constraintLevel >= 0) {
|
|
||||||
logger.debug(
|
|
||||||
"Setting the NIO constraint level to: " + constraintLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (constraintLevel < 0) {
|
|
||||||
constraintLevel = detectConstraintLevelFromSystemProperties();
|
|
||||||
|
|
||||||
if (constraintLevel < 0) {
|
|
||||||
constraintLevel = 2;
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug(
|
|
||||||
"Couldn't determine the NIO constraint level from " +
|
|
||||||
"the system properties; using the safest level (2)");
|
|
||||||
}
|
|
||||||
} else if (constraintLevel != 0) {
|
|
||||||
if (logger.isInfoEnabled()) {
|
|
||||||
logger.info(
|
|
||||||
"Using the autodetected NIO constraint level: " +
|
|
||||||
constraintLevel +
|
|
||||||
" (Use better NIO provider for better performance)");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug(
|
|
||||||
"Using the autodetected NIO constraint level: " +
|
|
||||||
constraintLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CONSTRAINT_LEVEL = constraintLevel;
|
|
||||||
|
|
||||||
if (CONSTRAINT_LEVEL < 0 || CONSTRAINT_LEVEL > 2) {
|
|
||||||
throw new Error(
|
|
||||||
"Unexpected NIO constraint level: " +
|
|
||||||
CONSTRAINT_LEVEL + ", please report this error.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int detectConstraintLevelFromSystemProperties() {
|
|
||||||
String version = SystemPropertyUtil.get("java.specification.version");
|
|
||||||
String vminfo = SystemPropertyUtil.get("java.vm.info", "");
|
|
||||||
String os = SystemPropertyUtil.get("os.name");
|
|
||||||
String vendor = SystemPropertyUtil.get("java.vm.vendor");
|
|
||||||
String provider;
|
|
||||||
try {
|
|
||||||
provider = SelectorProvider.provider().getClass().getName();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Perhaps security exception.
|
|
||||||
provider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version == null || os == null || vendor == null || provider == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
os = os.toLowerCase();
|
|
||||||
vendor = vendor.toLowerCase();
|
|
||||||
|
|
||||||
// System.out.println(version);
|
|
||||||
// System.out.println(vminfo);
|
|
||||||
// System.out.println(os);
|
|
||||||
// System.out.println(vendor);
|
|
||||||
// System.out.println(provider);
|
|
||||||
|
|
||||||
// Sun JVM
|
|
||||||
if (vendor.indexOf("sun") >= 0) {
|
|
||||||
// Linux
|
|
||||||
if (os.indexOf("linux") >= 0) {
|
|
||||||
if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
|
|
||||||
provider.equals("sun.nio.ch.PollSelectorProvider")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows
|
|
||||||
} else if (os.indexOf("windows") >= 0) {
|
|
||||||
if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Solaris
|
|
||||||
} else if (os.indexOf("sun") >= 0 || os.indexOf("solaris") >= 0) {
|
|
||||||
if (provider.equals("sun.nio.ch.DevPollSelectorProvider")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Apple JVM
|
|
||||||
} else if (vendor.indexOf("apple") >= 0) {
|
|
||||||
// Mac OS
|
|
||||||
if (os.indexOf("mac") >= 0 && os.indexOf("os") >= 0) {
|
|
||||||
if (provider.equals("sun.nio.ch.KQueueSelectorProvider")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// IBM
|
|
||||||
} else if (vendor.indexOf("ibm") >= 0) {
|
|
||||||
// Linux or AIX
|
|
||||||
if (os.indexOf("linux") >= 0 || os.indexOf("aix") >= 0) {
|
|
||||||
if (version.equals("1.5") || version.matches("^1\\.5\\D.*$")) {
|
|
||||||
if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if (version.equals("1.6") || version.matches("^1\\.6\\D.*$")) {
|
|
||||||
// IBM JDK 1.6 has different constraint level for different
|
|
||||||
// version. The exact version can be determined only by its
|
|
||||||
// build date.
|
|
||||||
Pattern datePattern = Pattern.compile(
|
|
||||||
"(?:^|[^0-9])(" +
|
|
||||||
"[2-9][0-9]{3}" + // year
|
|
||||||
"(?:0[1-9]|1[0-2])" + // month
|
|
||||||
"(?:0[1-9]|[12][0-9]|3[01])" + // day of month
|
|
||||||
")(?:$|[^0-9])");
|
|
||||||
|
|
||||||
Matcher dateMatcher = datePattern.matcher(vminfo);
|
|
||||||
if (dateMatcher.find()) {
|
|
||||||
long dateValue = Long.parseLong(dateMatcher.group(1));
|
|
||||||
if (dateValue < 20081105L) {
|
|
||||||
// SR0, 1, and 2
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
// SR3 and later
|
|
||||||
if (provider.equals("sun.nio.ch.EPollSelectorProvider")) {
|
|
||||||
return 0;
|
|
||||||
} else if (provider.equals("sun.nio.ch.PollSelectorProvider")) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// BEA
|
|
||||||
} else if (vendor.indexOf("bea") >= 0 || vendor.indexOf("oracle") >= 0) {
|
|
||||||
// Linux
|
|
||||||
if (os.indexOf("linux") >= 0) {
|
|
||||||
if (provider.equals("sun.nio.ch.EPollSelectorProvider") ||
|
|
||||||
provider.equals("sun.nio.ch.PollSelectorProvider")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows
|
|
||||||
} else if (os.indexOf("windows") >= 0) {
|
|
||||||
if (provider.equals("sun.nio.ch.WindowsSelectorProvider")) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Apache Software Foundation
|
|
||||||
} else if (vendor.indexOf("apache") >= 0) {
|
|
||||||
if (provider.equals("org.apache.harmony.nio.internal.SelectorProviderImpl")) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Others (untested)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ConstraintLevelAutodetector {
|
|
||||||
|
|
||||||
ConstraintLevelAutodetector() {
|
|
||||||
}
|
|
||||||
|
|
||||||
int autodetect() {
|
|
||||||
final int constraintLevel;
|
|
||||||
ExecutorService executor = Executors.newCachedThreadPool();
|
|
||||||
boolean success;
|
|
||||||
long startTime;
|
|
||||||
int interestOps;
|
|
||||||
|
|
||||||
ServerSocketChannel ch = null;
|
|
||||||
SelectorLoop loop = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Open a channel.
|
|
||||||
ch = ServerSocketChannel.open();
|
|
||||||
|
|
||||||
// Configure the channel
|
|
||||||
try {
|
|
||||||
ch.socket().bind(new InetSocketAddress(0));
|
|
||||||
ch.configureBlocking(false);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn("Failed to configure a temporary socket.", e);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the selector loop.
|
|
||||||
try {
|
|
||||||
loop = new SelectorLoop();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn("Failed to open a temporary selector.", e);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the channel
|
|
||||||
try {
|
|
||||||
ch.register(loop.selector, 0);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn("Failed to register a temporary selector.", e);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectionKey key = ch.keyFor(loop.selector);
|
|
||||||
|
|
||||||
// Start the selector loop.
|
|
||||||
executor.execute(loop);
|
|
||||||
|
|
||||||
// Level 0
|
|
||||||
success = true;
|
|
||||||
for (int i = 0; i < 10; i ++) {
|
|
||||||
|
|
||||||
// Increase the probability of calling interestOps
|
|
||||||
// while select() is running.
|
|
||||||
do {
|
|
||||||
while (!loop.selecting) {
|
|
||||||
Thread.yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait a little bit more.
|
|
||||||
try {
|
|
||||||
Thread.sleep(50);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
} while (!loop.selecting);
|
|
||||||
|
|
||||||
startTime = System.nanoTime();
|
|
||||||
key.interestOps(key.interestOps() | SelectionKey.OP_ACCEPT);
|
|
||||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_ACCEPT);
|
|
||||||
|
|
||||||
if (System.nanoTime() - startTime >= 500000000L) {
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
constraintLevel = 0;
|
|
||||||
} else {
|
|
||||||
// Level 1
|
|
||||||
success = true;
|
|
||||||
for (int i = 0; i < 10; i ++) {
|
|
||||||
|
|
||||||
// Increase the probability of calling interestOps
|
|
||||||
// while select() is running.
|
|
||||||
do {
|
|
||||||
while (!loop.selecting) {
|
|
||||||
Thread.yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait a little bit more.
|
|
||||||
try {
|
|
||||||
Thread.sleep(50);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
} while (!loop.selecting);
|
|
||||||
|
|
||||||
startTime = System.nanoTime();
|
|
||||||
interestOps = key.interestOps();
|
|
||||||
synchronized (loop) {
|
|
||||||
loop.selector.wakeup();
|
|
||||||
key.interestOps(interestOps | SelectionKey.OP_ACCEPT);
|
|
||||||
key.interestOps(interestOps & ~SelectionKey.OP_ACCEPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (System.nanoTime() - startTime >= 500000000L) {
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
constraintLevel = 1;
|
|
||||||
} else {
|
|
||||||
constraintLevel = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
return -1;
|
|
||||||
} finally {
|
|
||||||
if (ch != null) {
|
|
||||||
try {
|
|
||||||
ch.close();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn("Failed to close a temporary socket.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loop != null) {
|
|
||||||
loop.done = true;
|
|
||||||
try {
|
|
||||||
executor.shutdownNow();
|
|
||||||
} catch (NullPointerException ex) {
|
|
||||||
// Some JDK throws NPE here, but shouldn't.
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (;;) {
|
|
||||||
loop.selector.wakeup();
|
|
||||||
try {
|
|
||||||
if (executor.awaitTermination(1, TimeUnit.SECONDS)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// Perhaps security exception.
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
loop.selector.close();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn("Failed to close a temporary selector.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return constraintLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class SelectorLoop implements Runnable {
|
|
||||||
final Selector selector;
|
|
||||||
volatile boolean done;
|
|
||||||
volatile boolean selecting; // Just an approximation
|
|
||||||
|
|
||||||
SelectorLoop() throws IOException {
|
|
||||||
selector = Selector.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (!done) {
|
|
||||||
synchronized (this) {
|
|
||||||
// Guard
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
selecting = true;
|
|
||||||
try {
|
|
||||||
selector.select(1000);
|
|
||||||
} finally {
|
|
||||||
selecting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<SelectionKey> keys = selector.selectedKeys();
|
|
||||||
for (SelectionKey k: keys) {
|
|
||||||
k.interestOps(0);
|
|
||||||
}
|
|
||||||
keys.clear();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn("Failed to wait for a temporary selector.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
for (Entry<Object, Object> e: System.getProperties().entrySet()) {
|
|
||||||
System.out.println(e.getKey() + ": " + e.getValue());
|
|
||||||
}
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Hard-coded Constraint Level: " + CONSTRAINT_LEVEL);
|
|
||||||
System.out.println(
|
|
||||||
"Auto-detected Constraint Level: " +
|
|
||||||
new ConstraintLevelAutodetector().autodetect());
|
|
||||||
}
|
|
||||||
|
|
||||||
private NioProviderMetadata() {
|
|
||||||
// Unused
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,54 +15,32 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.channel.socket.nio;
|
package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.channels.ServerSocketChannel;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
import io.netty.channel.AbstractServerChannel;
|
import io.netty.channel.AbstractServerChannel;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFactory;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
|
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
|
||||||
import io.netty.channel.socket.ServerSocketChannelConfig;
|
import io.netty.channel.socket.ServerSocketChannelConfig;
|
||||||
import io.netty.logging.InternalLogger;
|
import io.netty.logging.InternalLogger;
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
import io.netty.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.channels.SelectionKey;
|
||||||
|
import java.nio.channels.ServerSocketChannel;
|
||||||
|
|
||||||
final class NioServerSocketChannel extends AbstractServerChannel
|
final class NioServerSocketChannel extends AbstractServerChannel
|
||||||
implements io.netty.channel.socket.ServerSocketChannel, NioChannel {
|
implements io.netty.channel.socket.ServerSocketChannel {
|
||||||
|
|
||||||
private static final InternalLogger logger =
|
private static final InternalLogger logger =
|
||||||
InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
|
InternalLoggerFactory.getInstance(NioServerSocketChannel.class);
|
||||||
|
|
||||||
final ServerSocketChannel socket;
|
private final ServerSocketChannel socket;
|
||||||
final Lock shutdownLock = new ReentrantLock();
|
|
||||||
final NioWorker worker;
|
|
||||||
final WorkerPool<NioWorker> workers;
|
|
||||||
|
|
||||||
|
|
||||||
private final ServerSocketChannelConfig config;
|
private final ServerSocketChannelConfig config;
|
||||||
|
private volatile InetSocketAddress localAddress;
|
||||||
|
private volatile SelectionKey selectionKey;
|
||||||
|
|
||||||
static NioServerSocketChannel create(ChannelFactory factory,
|
public NioServerSocketChannel() {
|
||||||
ChannelPipeline pipeline, ChannelSink sink, NioWorker worker, WorkerPool<NioWorker> workers) {
|
|
||||||
NioServerSocketChannel instance =
|
|
||||||
new NioServerSocketChannel(factory, pipeline, sink, worker, workers);
|
|
||||||
fireChannelOpen(instance);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NioServerSocketChannel(
|
|
||||||
ChannelFactory factory,
|
|
||||||
ChannelPipeline pipeline,
|
|
||||||
ChannelSink sink, NioWorker worker, WorkerPool<NioWorker> workers) {
|
|
||||||
|
|
||||||
super(factory, pipeline, sink);
|
|
||||||
this.worker = worker;
|
|
||||||
this.workers = workers;
|
|
||||||
try {
|
try {
|
||||||
socket = ServerSocketChannel.open();
|
socket = ServerSocketChannel.open();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -90,32 +68,114 @@ final class NioServerSocketChannel extends AbstractServerChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ServerSocketChannelConfig getConfig() {
|
public ServerSocketChannelConfig config() {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress getLocalAddress() {
|
public boolean isActive() {
|
||||||
return (InetSocketAddress) socket.socket().getLocalSocketAddress();
|
// TODO Auto-generated method stub
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress getRemoteAddress() {
|
public InetSocketAddress localAddress() {
|
||||||
|
InetSocketAddress localAddress = this.localAddress;
|
||||||
|
if (localAddress == null) {
|
||||||
|
try {
|
||||||
|
this.localAddress = localAddress =
|
||||||
|
(InetSocketAddress) unsafe().localAddress();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Sometimes fails on a closed socket in Windows.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetSocketAddress remoteAddress() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBound() {
|
protected java.nio.channels.ServerSocketChannel javaChannel() {
|
||||||
return isOpen() && socket.socket().isBound();
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean setClosed() {
|
protected SocketAddress localAddress0() {
|
||||||
return super.setClosed();
|
return socket.socket().getLocalSocketAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NioWorker getWorker() {
|
protected void doRegister(ChannelFuture future) {
|
||||||
return worker;
|
if (!(eventLoop() instanceof SelectorEventLoop)) {
|
||||||
|
throw new ChannelException("unsupported event loop: " + eventLoop().getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectorEventLoop loop = (SelectorEventLoop) eventLoop();
|
||||||
|
try {
|
||||||
|
selectionKey = javaChannel().register(loop.selector, javaChannel().validOps(), this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ChannelException("failed to register a channel", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(SocketAddress localAddress, ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
javaChannel().socket().bind(localAddress);
|
||||||
|
future.setSuccess();
|
||||||
|
pipeline().fireChannelActive();
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doClose(ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
javaChannel().close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to close a channel.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
future.setSuccess();
|
||||||
|
pipeline().fireChannelInactive();
|
||||||
|
|
||||||
|
if (isRegistered()) {
|
||||||
|
deregister(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeregister(ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
selectionKey.cancel();
|
||||||
|
future.setSuccess();
|
||||||
|
pipeline().fireChannelUnregistered();
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doRead() {
|
||||||
|
int acceptedConns = 0;
|
||||||
|
for (;;) {
|
||||||
|
try {
|
||||||
|
java.nio.channels.SocketChannel ch = javaChannel().accept();
|
||||||
|
if (ch == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pipeline().nextIn().messageBuffer().add(new NioSocketChannel(this, ch));
|
||||||
|
} catch (ChannelException e) {
|
||||||
|
pipeline().fireExceptionCaught(e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
pipeline().fireExceptionCaught(new ChannelException("failed to accept a connection", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acceptedConns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.nio.channels.Selector;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelSink;
|
|
||||||
import io.netty.channel.group.ChannelGroup;
|
|
||||||
import io.netty.channel.socket.ServerSocketChannel;
|
|
||||||
import io.netty.channel.socket.ServerSocketChannelFactory;
|
|
||||||
import io.netty.channel.socket.Worker;
|
|
||||||
import io.netty.util.ExternalResourceReleasable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ServerSocketChannelFactory} which creates a server-side NIO-based
|
|
||||||
* {@link ServerSocketChannel}. It utilizes the non-blocking I/O mode which
|
|
||||||
* was introduced with NIO to serve many number of concurrent connections
|
|
||||||
* efficiently.
|
|
||||||
*
|
|
||||||
* <h3>How threads work</h3>
|
|
||||||
* <p>
|
|
||||||
* There are two types of threads in a {@link NioServerSocketChannelFactory};
|
|
||||||
* one is boss thread and the other is worker thread.
|
|
||||||
*
|
|
||||||
* <h4>Boss threads</h4>
|
|
||||||
* <p>
|
|
||||||
* Each bound {@link ServerSocketChannel} has its own boss thread.
|
|
||||||
* For example, if you opened two server ports such as 80 and 443, you will
|
|
||||||
* have two boss threads. A boss thread accepts incoming connections until
|
|
||||||
* the port is unbound. Once a connection is accepted successfully, the boss
|
|
||||||
* thread passes the accepted {@link Channel} to one of the worker
|
|
||||||
* threads that the {@link NioServerSocketChannelFactory} manages.
|
|
||||||
*
|
|
||||||
* <h4>Worker threads</h4>
|
|
||||||
* <p>
|
|
||||||
* One {@link NioServerSocketChannelFactory} can have one or more worker
|
|
||||||
* threads. A worker thread performs non-blocking read and write for one or
|
|
||||||
* more {@link Channel}s in a non-blocking mode.
|
|
||||||
*
|
|
||||||
* <h3>Life cycle of threads and graceful shutdown</h3>
|
|
||||||
* <p>
|
|
||||||
* All threads are acquired from the {@link Executor}s which were specified
|
|
||||||
* when a {@link NioServerSocketChannelFactory} was created. Boss threads are
|
|
||||||
* acquired from the {@code bossExecutor}, and worker threads are acquired from
|
|
||||||
* the {@code workerExecutor}. Therefore, you should make sure the specified
|
|
||||||
* {@link Executor}s are able to lend the sufficient number of threads.
|
|
||||||
* It is the best bet to specify {@linkplain Executors#newCachedThreadPool() a cached thread pool}.
|
|
||||||
* <p>
|
|
||||||
* Both boss and worker threads are acquired lazily, and then released when
|
|
||||||
* there's nothing left to process. All the related resources such as
|
|
||||||
* {@link Selector} are also released when the boss and worker threads are
|
|
||||||
* released. Therefore, to shut down a service gracefully, you should do the
|
|
||||||
* following:
|
|
||||||
*
|
|
||||||
* <ol>
|
|
||||||
* <li>unbind all channels created by the factory,
|
|
||||||
* <li>close all child channels accepted by the unbound channels, and
|
|
||||||
* (these two steps so far is usually done using {@link ChannelGroup#close()})</li>
|
|
||||||
* <li>call {@link #releaseExternalResources()}.</li>
|
|
||||||
* </ol>
|
|
||||||
*
|
|
||||||
* Please make sure not to shut down the executor until all channels are
|
|
||||||
* closed. Otherwise, you will end up with a {@link RejectedExecutionException}
|
|
||||||
* and the related resources might not be released properly.
|
|
||||||
* @apiviz.landmark
|
|
||||||
*/
|
|
||||||
public class NioServerSocketChannelFactory implements ServerSocketChannelFactory {
|
|
||||||
|
|
||||||
private final WorkerPool<NioWorker> workerPool;
|
|
||||||
private final ChannelSink sink;
|
|
||||||
private final WorkerPool<NioWorker> bossWorkerPool;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link NioServerSocketChannelFactory} using
|
|
||||||
* {@link Executors#newCachedThreadPool()} for the workers.
|
|
||||||
*
|
|
||||||
* See {@link #NioServerSocketChannelFactory(Executor, Executor)}
|
|
||||||
*/
|
|
||||||
public NioServerSocketChannelFactory() {
|
|
||||||
this(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance. Calling this constructor is same with calling
|
|
||||||
* {@link #NioServerSocketChannelFactory(Executor, Executor, int, int)} with 1
|
|
||||||
* as boss count and 2 * the number of available processors in the machine. The number of
|
|
||||||
* available processors is obtained by {@link Runtime#availableProcessors()}.
|
|
||||||
*
|
|
||||||
* @param bossExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads that handle the accepting of new connections
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads
|
|
||||||
*/
|
|
||||||
public NioServerSocketChannelFactory(Executor bossExecutor, Executor workerExecutor) {
|
|
||||||
this(bossExecutor, workerExecutor, SelectorUtil.DEFAULT_IO_ACCEPTING_THREADS, SelectorUtil.DEFAULT_IO_THREADS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param bossExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads that handle the accepting of new connections
|
|
||||||
* @param workerExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads
|
|
||||||
* @param bossCount
|
|
||||||
* the maximum number of I/O worker threads that handling the accepting of connections
|
|
||||||
* @param workerCount
|
|
||||||
* the maximum number of I/O worker threads
|
|
||||||
*/
|
|
||||||
public NioServerSocketChannelFactory(Executor bossExecutor, Executor workerExecutor, int bossCount,
|
|
||||||
int workerCount) {
|
|
||||||
this(new NioWorkerPool(bossExecutor, bossCount, true), new NioWorkerPool(workerExecutor, workerCount, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param bossWorkerPool
|
|
||||||
* the {@link WorkerPool} which will be used to obtain the {@link Worker} that execute the I/O worker threads that handle the accepting of new connections
|
|
||||||
* @param workerPool
|
|
||||||
* the {@link WorkerPool} which will be used to obtain the {@link Worker} that execute the I/O worker threads
|
|
||||||
*/
|
|
||||||
public NioServerSocketChannelFactory(WorkerPool<NioWorker> bossWorkerPool, WorkerPool<NioWorker> workerPool) {
|
|
||||||
if (bossWorkerPool == null) {
|
|
||||||
throw new NullPointerException("bossWorkerPool");
|
|
||||||
}
|
|
||||||
if (workerPool == null) {
|
|
||||||
throw new NullPointerException("workerPool");
|
|
||||||
}
|
|
||||||
this.bossWorkerPool = bossWorkerPool;
|
|
||||||
this.workerPool = workerPool;
|
|
||||||
sink = new NioServerSocketPipelineSink();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance which use the given {@link WorkerPool} for everything.
|
|
||||||
*
|
|
||||||
* @param genericExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads ( this also includes handle the accepting of new connections)
|
|
||||||
* @param workerCount
|
|
||||||
* the maximum number of I/O worker threads
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public NioServerSocketChannelFactory(Executor genericExecutor, int workerCount) {
|
|
||||||
this(new NioWorkerPool(genericExecutor, workerCount, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance which use the given {@link WorkerPool} for everything.
|
|
||||||
*
|
|
||||||
* @param genericExecutor
|
|
||||||
* the {@link Executor} which will execute the I/O worker threads ( this also includes handle the accepting of new connections)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public NioServerSocketChannelFactory(Executor genericExecutor) {
|
|
||||||
this(genericExecutor, SelectorUtil.DEFAULT_IO_ACCEPTING_THREADS + SelectorUtil.DEFAULT_IO_THREADS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance which use the given {@link WorkerPool} for everything.
|
|
||||||
*
|
|
||||||
* @param genericWorkerPool
|
|
||||||
* the {@link WorkerPool} which will be used to obtain the {@link Worker} that execute the I/O worker threads (that included accepting of new connections)
|
|
||||||
*/
|
|
||||||
public NioServerSocketChannelFactory(WorkerPool<NioWorker> genericWorkerPool) {
|
|
||||||
this(genericWorkerPool, genericWorkerPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServerSocketChannel newChannel(ChannelPipeline pipeline) {
|
|
||||||
return NioServerSocketChannel.create(this, pipeline, sink, bossWorkerPool.nextWorker(), workerPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void releaseExternalResources() {
|
|
||||||
if (workerPool instanceof ExternalResourceReleasable) {
|
|
||||||
((ExternalResourceReleasable) workerPool).releaseExternalResources();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.*;
|
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelEvent;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.ChannelState;
|
|
||||||
import io.netty.channel.ChannelStateEvent;
|
|
||||||
import io.netty.channel.MessageEvent;
|
|
||||||
import io.netty.logging.InternalLogger;
|
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
|
||||||
|
|
||||||
class NioServerSocketPipelineSink extends AbstractNioChannelSink {
|
|
||||||
|
|
||||||
static final InternalLogger logger =
|
|
||||||
InternalLoggerFactory.getInstance(NioServerSocketPipelineSink.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventSunk(
|
|
||||||
ChannelPipeline pipeline, ChannelEvent e) throws Exception {
|
|
||||||
Channel channel = e.getChannel();
|
|
||||||
if (channel instanceof NioServerSocketChannel) {
|
|
||||||
handleServerSocket(e);
|
|
||||||
} else if (channel instanceof NioSocketChannel) {
|
|
||||||
handleAcceptedSocket(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleServerSocket(ChannelEvent e) {
|
|
||||||
if (!(e instanceof ChannelStateEvent)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelStateEvent event = (ChannelStateEvent) e;
|
|
||||||
NioServerSocketChannel channel =
|
|
||||||
(NioServerSocketChannel) event.channel();
|
|
||||||
ChannelFuture future = event.getFuture();
|
|
||||||
ChannelState state = event.getState();
|
|
||||||
Object value = event.getValue();
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case OPEN:
|
|
||||||
if (Boolean.FALSE.equals(value)) {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BOUND:
|
|
||||||
if (value != null) {
|
|
||||||
bind(channel, future, (SocketAddress) value);
|
|
||||||
} else {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleAcceptedSocket(ChannelEvent e) {
|
|
||||||
if (e instanceof ChannelStateEvent) {
|
|
||||||
ChannelStateEvent event = (ChannelStateEvent) e;
|
|
||||||
NioSocketChannel channel = (NioSocketChannel) event.channel();
|
|
||||||
ChannelFuture future = event.getFuture();
|
|
||||||
ChannelState state = event.getState();
|
|
||||||
Object value = event.getValue();
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case OPEN:
|
|
||||||
if (Boolean.FALSE.equals(value)) {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BOUND:
|
|
||||||
case CONNECTED:
|
|
||||||
if (value == null) {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INTEREST_OPS:
|
|
||||||
channel.getWorker().setInterestOps(channel, future, ((Integer) value).intValue());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (e instanceof MessageEvent) {
|
|
||||||
MessageEvent event = (MessageEvent) e;
|
|
||||||
NioSocketChannel channel = (NioSocketChannel) event.channel();
|
|
||||||
boolean offered = channel.writeBufferQueue.offer(event);
|
|
||||||
assert offered;
|
|
||||||
channel.getWorker().writeFromUserCode(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(
|
|
||||||
NioServerSocketChannel channel, ChannelFuture future,
|
|
||||||
SocketAddress localAddress) {
|
|
||||||
boolean bound = false;
|
|
||||||
try {
|
|
||||||
channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog());
|
|
||||||
bound = true;
|
|
||||||
|
|
||||||
future.setSuccess();
|
|
||||||
fireChannelBound(channel, channel.getLocalAddress());
|
|
||||||
|
|
||||||
channel.getWorker().registerWithWorker(channel, future);
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} finally {
|
|
||||||
if (!bound) {
|
|
||||||
channel.getWorker().close(channel, future);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,84 +15,274 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.channel.socket.nio;
|
package io.netty.channel.socket.nio;
|
||||||
|
|
||||||
|
import io.netty.buffer.ChannelBuffer;
|
||||||
|
import io.netty.buffer.ChannelBuffers;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFactory;
|
import io.netty.channel.ChannelBufferHolder;
|
||||||
|
import io.netty.channel.ChannelBufferHolders;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.logging.InternalLogger;
|
||||||
import io.netty.channel.ChannelSink;
|
import io.netty.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.channels.AsynchronousCloseException;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.nio.channels.SelectionKey;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
|
|
||||||
public abstract class NioSocketChannel extends AbstractNioChannel implements io.netty.channel.socket.SocketChannel {
|
public class NioSocketChannel extends AbstractNioChannel implements io.netty.channel.socket.SocketChannel {
|
||||||
|
|
||||||
private static final int ST_OPEN = 0;
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioSocketChannel.class);
|
||||||
private static final int ST_BOUND = 1;
|
|
||||||
private static final int ST_CONNECTED = 2;
|
|
||||||
private static final int ST_CLOSED = -1;
|
|
||||||
volatile int state = ST_OPEN;
|
|
||||||
|
|
||||||
private final NioSocketChannelConfig config;
|
private final NioSocketChannelConfig config;
|
||||||
|
private final ChannelBufferHolder<?> out = ChannelBufferHolders.byteBuffer(ChannelBuffers.dynamicBuffer());
|
||||||
|
|
||||||
public NioSocketChannel(
|
private static SocketChannel newSocket() {
|
||||||
Channel parent, ChannelFactory factory,
|
SocketChannel socket;
|
||||||
ChannelPipeline pipeline, ChannelSink sink,
|
try {
|
||||||
SocketChannel socket, NioWorker worker) {
|
socket = SocketChannel.open();
|
||||||
super(parent, factory, pipeline, sink, worker, new NioSocketJdkChannel(socket));
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException("Failed to open a socket.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
socket.configureBlocking(false);
|
||||||
|
success = true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException("Failed to enter non-blocking mode.", e);
|
||||||
|
} finally {
|
||||||
|
if (!success) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn(
|
||||||
|
"Failed to close a partially initialized socket.",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NioSocketChannel(Channel parent) {
|
||||||
|
this(parent, newSocket());
|
||||||
|
}
|
||||||
|
|
||||||
|
public NioSocketChannel(Channel parent, SocketChannel socket) {
|
||||||
|
super(parent, socket);
|
||||||
config = new DefaultNioSocketChannelConfig(socket.socket());
|
config = new DefaultNioSocketChannelConfig(socket.socket());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NioWorker getWorker() {
|
public NioSocketChannelConfig config() {
|
||||||
return (NioWorker) super.getWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NioSocketChannelConfig getConfig() {
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOpen() {
|
protected SocketChannel javaChannel() {
|
||||||
return state >= ST_OPEN;
|
return (SocketChannel) super.javaChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBound() {
|
public boolean isActive() {
|
||||||
return state >= ST_BOUND;
|
return javaChannel().isConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected ChannelBufferHolder<Object> firstOut() {
|
||||||
|
return (ChannelBufferHolder<Object>) out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isConnected() {
|
protected SocketAddress localAddress0() {
|
||||||
return state == ST_CONNECTED;
|
return javaChannel().socket().getLocalSocketAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
final void setBound() {
|
@Override
|
||||||
assert state == ST_OPEN : "Invalid state: " + state;
|
protected SocketAddress remoteAddress0() {
|
||||||
state = ST_BOUND;
|
return javaChannel().socket().getRemoteSocketAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
final void setConnected() {
|
@Override
|
||||||
if (state != ST_CLOSED) {
|
protected void doBind(SocketAddress localAddress, ChannelFuture future) {
|
||||||
state = ST_CONNECTED;
|
try {
|
||||||
|
javaChannel().socket().bind(localAddress);
|
||||||
|
future.setSuccess();
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.setFailure(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean setClosed() {
|
protected void doConnect(SocketAddress remoteAddress,
|
||||||
state = ST_CLOSED;
|
SocketAddress localAddress, ChannelFuture future) {
|
||||||
return super.setClosed();
|
if (localAddress != null) {
|
||||||
}
|
try {
|
||||||
|
javaChannel().socket().bind(localAddress);
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
try {
|
||||||
public ChannelFuture write(Object message, SocketAddress remoteAddress) {
|
if (javaChannel().connect(remoteAddress)) {
|
||||||
if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) {
|
future.setSuccess();
|
||||||
return super.write(message, null);
|
pipeline().fireChannelActive();
|
||||||
} else {
|
}
|
||||||
return getUnsupportedOperationFuture();
|
} catch (Exception e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
close(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFinishConnect(ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
if (javaChannel().finishConnect()) {
|
||||||
|
future.setSuccess();
|
||||||
|
pipeline().fireChannelActive();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
close(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDisconnect(ChannelFuture future) {
|
||||||
|
doClose(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doClose(ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
javaChannel().close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Failed to close a channel.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
future.setSuccess();
|
||||||
|
pipeline().fireChannelInactive();
|
||||||
|
|
||||||
|
if (isRegistered()) {
|
||||||
|
deregister(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeregister(ChannelFuture future) {
|
||||||
|
try {
|
||||||
|
selectionKey().cancel();
|
||||||
|
future.setSuccess();
|
||||||
|
pipeline().fireChannelUnregistered();
|
||||||
|
} catch (Exception e) {
|
||||||
|
future.setFailure(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doRead() {
|
||||||
|
final SocketChannel ch = javaChannel();
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
int readBytes = 0;
|
||||||
|
boolean failure = true;
|
||||||
|
|
||||||
|
ChannelBuffer buf = pipeline().nextIn().byteBuffer();
|
||||||
|
try {
|
||||||
|
while ((ret = buf.writeBytes(ch, buf.writableBytes())) > 0) {
|
||||||
|
readBytes += ret;
|
||||||
|
if (!buf.writable()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
failure = false;
|
||||||
|
} catch (ClosedChannelException e) {
|
||||||
|
// Can happen, and does not need a user attention.
|
||||||
|
} catch (Throwable t) {
|
||||||
|
pipeline().fireExceptionCaught(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readBytes > 0) {
|
||||||
|
pipeline().fireInboundBufferUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0 || failure) {
|
||||||
|
selectionKey().cancel(); // Some JDK implementations run into an infinite loop without this.
|
||||||
|
close(null);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return readBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int doFlush(ChannelFuture future) {
|
||||||
|
boolean open = true;
|
||||||
|
boolean addOpWrite = false;
|
||||||
|
boolean removeOpWrite = false;
|
||||||
|
|
||||||
|
final SocketChannel ch = javaChannel();
|
||||||
|
final int writeSpinCount = config().getWriteSpinCount();
|
||||||
|
final ChannelBuffer buf = unsafe().out().byteBuffer();
|
||||||
|
int bytesLeft = buf.readableBytes();
|
||||||
|
if (bytesLeft == 0) {
|
||||||
|
future.setSuccess();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readerIndex = buf.readerIndex();
|
||||||
|
int localWrittenBytes = 0;
|
||||||
|
int writtenBytes = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (int i = writeSpinCount; i > 0; i --) {
|
||||||
|
localWrittenBytes = buf.getBytes(readerIndex, ch, bytesLeft);
|
||||||
|
if (localWrittenBytes > 0) {
|
||||||
|
bytesLeft -= localWrittenBytes;
|
||||||
|
if (bytesLeft <= 0) {
|
||||||
|
removeOpWrite = true;
|
||||||
|
future.setSuccess();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
readerIndex += localWrittenBytes;
|
||||||
|
writtenBytes += localWrittenBytes;
|
||||||
|
} else {
|
||||||
|
addOpWrite = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (AsynchronousCloseException e) {
|
||||||
|
// Doesn't need a user attention - ignore.
|
||||||
|
} catch (Throwable t) {
|
||||||
|
future.setFailure(t);
|
||||||
|
pipeline().fireExceptionCaught(t);
|
||||||
|
if (t instanceof IOException) {
|
||||||
|
open = false;
|
||||||
|
close(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
if (addOpWrite) {
|
||||||
|
SelectionKey key = selectionKey();
|
||||||
|
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
|
||||||
|
} else if (removeOpWrite) {
|
||||||
|
SelectionKey key = selectionKey();
|
||||||
|
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writtenBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
|
|
||||||
class NioSocketJdkChannel extends AbstractJdkChannel {
|
|
||||||
|
|
||||||
|
|
||||||
public NioSocketJdkChannel(SocketChannel channel) {
|
|
||||||
super(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SocketChannel getChannel() {
|
|
||||||
return (SocketChannel) super.getChannel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress getRemoteSocketAddress() {
|
|
||||||
return (InetSocketAddress) getChannel().socket().getRemoteSocketAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InetSocketAddress getLocalSocketAddress() {
|
|
||||||
return (InetSocketAddress) getChannel().socket().getLocalSocketAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSocketBound() {
|
|
||||||
return getChannel().socket().isBound();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bind(SocketAddress local) throws IOException {
|
|
||||||
getChannel().socket().bind(local);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connect(SocketAddress remote) throws IOException {
|
|
||||||
getChannel().connect(remote);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConnected() {
|
|
||||||
return getChannel().isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disconnectSocket() throws IOException {
|
|
||||||
getChannel().socket().close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeSocket() throws IOException {
|
|
||||||
getChannel().socket().close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int write(ByteBuffer src) throws IOException {
|
|
||||||
return getChannel().write(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean finishConnect() throws IOException {
|
|
||||||
return getChannel().finishConnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import static io.netty.channel.Channels.fireChannelBound;
|
|
||||||
import static io.netty.channel.Channels.fireChannelConnected;
|
|
||||||
import static io.netty.channel.Channels.fireExceptionCaught;
|
|
||||||
import static io.netty.channel.Channels.fireMessageReceived;
|
|
||||||
import static io.netty.channel.Channels.succeededFuture;
|
|
||||||
import io.netty.buffer.ChannelBuffer;
|
|
||||||
import io.netty.buffer.ChannelBufferFactory;
|
|
||||||
import io.netty.channel.ChannelException;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ReceiveBufferSizePredictor;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.SocketAddress;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.nio.channels.SelectionKey;
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public class NioWorker extends SelectorEventLoop {
|
|
||||||
|
|
||||||
protected final ReceiveBufferPool recvBufferPool = new ReceiveBufferPool();
|
|
||||||
|
|
||||||
public NioWorker(Executor executor) {
|
|
||||||
super(executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NioWorker(Executor executor, boolean allowShutdownOnIdle) {
|
|
||||||
super(executor, allowShutdownOnIdle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean read(SelectionKey k) {
|
|
||||||
final SocketChannel ch = (SocketChannel) k.channel();
|
|
||||||
final NioSocketChannel channel = (NioSocketChannel) k.attachment();
|
|
||||||
|
|
||||||
final ReceiveBufferSizePredictor predictor =
|
|
||||||
channel.getConfig().getReceiveBufferSizePredictor();
|
|
||||||
final int predictedRecvBufSize = predictor.nextReceiveBufferSize();
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
int readBytes = 0;
|
|
||||||
boolean failure = true;
|
|
||||||
|
|
||||||
ByteBuffer bb = recvBufferPool.acquire(predictedRecvBufSize);
|
|
||||||
try {
|
|
||||||
while ((ret = ch.read(bb)) > 0) {
|
|
||||||
readBytes += ret;
|
|
||||||
if (!bb.hasRemaining()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
failure = false;
|
|
||||||
} catch (ClosedChannelException e) {
|
|
||||||
// Can happen, and does not need a user attention.
|
|
||||||
} catch (Throwable t) {
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readBytes > 0) {
|
|
||||||
bb.flip();
|
|
||||||
|
|
||||||
final ChannelBufferFactory bufferFactory =
|
|
||||||
channel.getConfig().getBufferFactory();
|
|
||||||
final ChannelBuffer buffer = bufferFactory.getBuffer(readBytes);
|
|
||||||
buffer.setBytes(0, bb);
|
|
||||||
buffer.writerIndex(readBytes);
|
|
||||||
|
|
||||||
recvBufferPool.release(bb);
|
|
||||||
|
|
||||||
// Update the predictor.
|
|
||||||
predictor.previousReceiveBufferSize(readBytes);
|
|
||||||
|
|
||||||
// Fire the event.
|
|
||||||
fireMessageReceived(channel, buffer);
|
|
||||||
} else {
|
|
||||||
recvBufferPool.release(bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0 || failure) {
|
|
||||||
k.cancel(); // Some JDK implementations run into an infinite loop without this.
|
|
||||||
close(channel, succeededFuture(channel));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void registerTask(AbstractNioChannel channel, ChannelFuture future) {
|
|
||||||
boolean server = !(channel instanceof NioClientSocketChannel);
|
|
||||||
SocketAddress localAddress = channel.getLocalAddress();
|
|
||||||
SocketAddress remoteAddress = channel.getRemoteAddress();
|
|
||||||
|
|
||||||
if (localAddress == null || remoteAddress == null) {
|
|
||||||
if (future != null) {
|
|
||||||
future.setFailure(new ClosedChannelException());
|
|
||||||
}
|
|
||||||
close(channel, succeededFuture(channel));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (server) {
|
|
||||||
channel.getJdkChannel().configureBlocking(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean registered = channel.getJdkChannel().isRegistered();
|
|
||||||
if (!registered) {
|
|
||||||
synchronized (channel.interestOpsLock) {
|
|
||||||
channel.getJdkChannel().register(selector, channel.getRawInterestOps(), channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
setInterestOps(channel, succeededFuture(channel), channel.getRawInterestOps());
|
|
||||||
}
|
|
||||||
if (future != null) {
|
|
||||||
if (channel instanceof NioSocketChannel) {
|
|
||||||
((NioSocketChannel) channel).setConnected();
|
|
||||||
}
|
|
||||||
future.setSuccess();
|
|
||||||
}
|
|
||||||
if (server || !((NioClientSocketChannel) channel).boundManually) {
|
|
||||||
fireChannelBound(channel, localAddress);
|
|
||||||
}
|
|
||||||
fireChannelConnected(channel, remoteAddress);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (future != null) {
|
|
||||||
future.setFailure(e);
|
|
||||||
}
|
|
||||||
close(channel, succeededFuture(channel));
|
|
||||||
if (!(e instanceof ClosedChannelException)) {
|
|
||||||
throw new ChannelException(
|
|
||||||
"Failed to register a socket to the selector.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default implementation which hands of {@link NioWorker}'s
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class NioWorkerPool extends AbstractNioWorkerPool<NioWorker> {
|
|
||||||
|
|
||||||
public NioWorkerPool(Executor executor, int workerCount, boolean allowShutdownOnIdle) {
|
|
||||||
super(executor, workerCount, allowShutdownOnIdle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected NioWorker createWorker(Executor executor, boolean allowShutdownOnIdle) {
|
|
||||||
return new NioWorker(executor, allowShutdownOnIdle);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2011 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.socket.nio;
|
|
||||||
|
|
||||||
import java.lang.ref.SoftReference;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
public final class ReceiveBufferPool {
|
|
||||||
|
|
||||||
private static final int POOL_SIZE = 8;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private final SoftReference<ByteBuffer>[] pool = new SoftReference[POOL_SIZE];
|
|
||||||
|
|
||||||
public ByteBuffer acquire(int size) {
|
|
||||||
final SoftReference<ByteBuffer>[] pool = this.pool;
|
|
||||||
for (int i = 0; i < POOL_SIZE; i ++) {
|
|
||||||
SoftReference<ByteBuffer> ref = pool[i];
|
|
||||||
if (ref == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer buf = ref.get();
|
|
||||||
if (buf == null) {
|
|
||||||
pool[i] = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf.capacity() < size) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pool[i] = null;
|
|
||||||
|
|
||||||
buf.clear();
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer buf = ByteBuffer.allocateDirect(normalizeCapacity(size));
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void release(ByteBuffer buffer) {
|
|
||||||
final SoftReference<ByteBuffer>[] pool = this.pool;
|
|
||||||
for (int i = 0; i < POOL_SIZE; i ++) {
|
|
||||||
SoftReference<ByteBuffer> ref = pool[i];
|
|
||||||
if (ref == null || ref.get() == null) {
|
|
||||||
pool[i] = new SoftReference<ByteBuffer>(buffer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pool is full - replace one
|
|
||||||
final int capacity = buffer.capacity();
|
|
||||||
for (int i = 0; i < POOL_SIZE; i ++) {
|
|
||||||
SoftReference<ByteBuffer> ref = pool[i];
|
|
||||||
ByteBuffer pooled = ref.get();
|
|
||||||
if (pooled == null) {
|
|
||||||
pool[i] = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pooled.capacity() < capacity) {
|
|
||||||
pool[i] = new SoftReference<ByteBuffer>(buffer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int normalizeCapacity(int capacity) {
|
|
||||||
// Normalize to multiple of 1024
|
|
||||||
int q = capacity >>> 10;
|
|
||||||
int r = capacity & 1023;
|
|
||||||
if (r != 0) {
|
|
||||||
q ++;
|
|
||||||
}
|
|
||||||
return q << 10;
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,35 +17,21 @@ package io.netty.channel.socket.nio;
|
|||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelException;
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPipeline;
|
|
||||||
import io.netty.channel.EventLoop;
|
|
||||||
import io.netty.channel.SingleThreadEventLoop;
|
import io.netty.channel.SingleThreadEventLoop;
|
||||||
import io.netty.channel.socket.nio.SendBufferPool.SendBuffer;
|
|
||||||
import io.netty.logging.InternalLogger;
|
import io.netty.logging.InternalLogger;
|
||||||
import io.netty.logging.InternalLoggerFactory;
|
import io.netty.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.nio.channels.AsynchronousCloseException;
|
|
||||||
import java.nio.channels.CancelledKeyException;
|
import java.nio.channels.CancelledKeyException;
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.nio.channels.ClosedSelectorException;
|
|
||||||
import java.nio.channels.NotYetConnectedException;
|
|
||||||
import java.nio.channels.SelectionKey;
|
import java.nio.channels.SelectionKey;
|
||||||
import java.nio.channels.Selector;
|
import java.nio.channels.Selector;
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
import java.nio.channels.WritableByteChannel;
|
|
||||||
import java.nio.channels.spi.SelectorProvider;
|
import java.nio.channels.spi.SelectorProvider;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
||||||
/**
|
/**
|
||||||
@ -54,11 +40,8 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
protected static final InternalLogger logger = InternalLoggerFactory
|
protected static final InternalLogger logger = InternalLoggerFactory
|
||||||
.getInstance(SelectorEventLoop.class);
|
.getInstance(SelectorEventLoop.class);
|
||||||
|
|
||||||
private static final int CONSTRAINT_LEVEL = NioProviderMetadata.CONSTRAINT_LEVEL;
|
|
||||||
|
|
||||||
static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization.
|
static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization.
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The NIO {@link Selector}.
|
* The NIO {@link Selector}.
|
||||||
*/
|
*/
|
||||||
@ -72,20 +55,8 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
*/
|
*/
|
||||||
protected final AtomicBoolean wakenUp = new AtomicBoolean();
|
protected final AtomicBoolean wakenUp = new AtomicBoolean();
|
||||||
|
|
||||||
/**
|
|
||||||
* Lock for this workers Selector.
|
|
||||||
*/
|
|
||||||
private final ReadWriteLock selectorGuard = new ReentrantReadWriteLock();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Monitor object used to synchronize selector open/close.
|
|
||||||
*/
|
|
||||||
private final Object startStopLock = new Object();
|
|
||||||
|
|
||||||
private volatile int cancelledKeys; // should use AtomicInteger but we just need approximation
|
private volatile int cancelledKeys; // should use AtomicInteger but we just need approximation
|
||||||
|
|
||||||
protected final SendBufferPool sendBufferPool = new SendBufferPool();
|
|
||||||
|
|
||||||
protected SelectorEventLoop() {
|
protected SelectorEventLoop() {
|
||||||
this(Executors.defaultThreadFactory());
|
this(Executors.defaultThreadFactory());
|
||||||
}
|
}
|
||||||
@ -114,69 +85,6 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public EventLoop register(final Channel channel, final ChannelFuture future) {
|
|
||||||
try {
|
|
||||||
if (channel instanceof NioServerSocketChannel) {
|
|
||||||
final NioServerSocketChannel ch = (NioServerSocketChannel) channel;
|
|
||||||
execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
ch.socket.register(selector, SelectionKey.OP_ACCEPT, ch);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (channel instanceof NioClientSocketChannel) {
|
|
||||||
final NioClientSocketChannel clientChannel = (NioClientSocketChannel) channel;
|
|
||||||
|
|
||||||
execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
clientChannel.getJdkChannel().register(selector, clientChannel.getRawInterestOps() | SelectionKey.OP_CONNECT, clientChannel);
|
|
||||||
} catch (ClosedChannelException ignored) {
|
|
||||||
clientChannel.getWorker().close(clientChannel, succeededFuture(channel));
|
|
||||||
}
|
|
||||||
int connectTimeout = channel.getConfig().getConnectTimeoutMillis();
|
|
||||||
if (connectTimeout > 0) {
|
|
||||||
clientChannel.connectDeadlineNanos = System.nanoTime() + connectTimeout * 1000000L;
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (channel instanceof AbstractNioChannel) {
|
|
||||||
execute(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
registerTask((AbstractNioChannel) channel, future);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new UnsupportedOperationException("Unable to handle channel " + channel);
|
|
||||||
}
|
|
||||||
if (wakenUp.compareAndSet(false, true)) {
|
|
||||||
selector.wakeup();
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
long lastConnectTimeoutCheckTimeNanos = System.nanoTime();
|
long lastConnectTimeoutCheckTimeNanos = System.nanoTime();
|
||||||
@ -186,13 +94,6 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
|
|
||||||
wakenUp.set(false);
|
wakenUp.set(false);
|
||||||
|
|
||||||
if (CONSTRAINT_LEVEL != 0) {
|
|
||||||
selectorGuard.writeLock().lock();
|
|
||||||
// This empty synchronization block prevents the selector
|
|
||||||
// from acquiring its lock.
|
|
||||||
selectorGuard.writeLock().unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SelectorUtil.select(selector);
|
SelectorUtil.select(selector);
|
||||||
|
|
||||||
@ -239,19 +140,9 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
processConnectTimeout(selector.keys(), currentTimeNanos);
|
processConnectTimeout(selector.keys(), currentTimeNanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit the loop when there's nothing to handle.
|
if (isShutdown()) {
|
||||||
// The shutdown flag is used to delay the shutdown of this
|
// FIXME: Close all channels immediately and break the loop.
|
||||||
// loop to avoid excessive Selector creation when
|
break;
|
||||||
// connections are registered in a one-by-one manner instead of
|
|
||||||
// concurrent manner.
|
|
||||||
if (selector.keys().isEmpty()) {
|
|
||||||
if (isShutdown()) {
|
|
||||||
synchronized (startStopLock) {
|
|
||||||
if (!hasTasks() && selector.keys().isEmpty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
@ -293,83 +184,42 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
private void processSelectedKeys(Set<SelectionKey> selectedKeys) throws IOException {
|
private void processSelectedKeys(Set<SelectionKey> selectedKeys) throws IOException {
|
||||||
for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
|
for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
|
||||||
SelectionKey k = i.next();
|
SelectionKey k = i.next();
|
||||||
|
Channel ch = (Channel) k.attachment();
|
||||||
boolean removeKey = true;
|
boolean removeKey = true;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
int readyOps = k.readyOps();
|
int readyOps = k.readyOps();
|
||||||
if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == 0) {
|
if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == 0) {
|
||||||
if (!read(k)) {
|
if (ch.unsafe().read() < 0) {
|
||||||
// Connection already closed - no need to handle write.
|
// Connection already closed - no need to handle write.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
|
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
|
||||||
writeFromSelectorLoop(k);
|
ch.unsafe().flush(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((readyOps & SelectionKey.OP_ACCEPT) != 0) {
|
if ((readyOps & SelectionKey.OP_ACCEPT) != 0) {
|
||||||
removeKey = accept(k);
|
ch.unsafe().read();
|
||||||
}
|
}
|
||||||
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
|
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
|
||||||
connect(k);
|
ch.unsafe().finishConnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (CancelledKeyException ignored) {
|
} catch (CancelledKeyException ignored) {
|
||||||
close(k);
|
ch.unsafe().close(null);
|
||||||
} finally {
|
} finally {
|
||||||
if (removeKey) {
|
if (removeKey) {
|
||||||
i.remove();
|
i.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (cleanUpCancelledKeys()) {
|
if (cleanUpCancelledKeys()) {
|
||||||
break; // break the loop to avoid ConcurrentModificationException
|
break; // break the loop to avoid ConcurrentModificationException
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean accept(SelectionKey key) {
|
|
||||||
NioServerSocketChannel channel = (NioServerSocketChannel) key.attachment();
|
|
||||||
try {
|
|
||||||
boolean handled = false;
|
|
||||||
|
|
||||||
// accept all sockets that are waiting atm
|
|
||||||
for (;;) {
|
|
||||||
SocketChannel acceptedSocket = channel.socket.accept();
|
|
||||||
if (acceptedSocket == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// TODO: Remove the casting stuff
|
|
||||||
ChannelPipeline pipeline =
|
|
||||||
channel.getConfig().getPipelineFactory().getPipeline();
|
|
||||||
NioWorker worker = channel.workers.nextWorker();
|
|
||||||
|
|
||||||
worker.registerWithWorker(NioAcceptedSocketChannel.create(channel.getFactory(), pipeline, channel,
|
|
||||||
channel.getPipeline().getSink(), acceptedSocket, worker), null);
|
|
||||||
handled = true;
|
|
||||||
}
|
|
||||||
return handled;
|
|
||||||
} catch (SocketTimeoutException e) {
|
|
||||||
// Thrown every second to get ClosedChannelException
|
|
||||||
// raised.
|
|
||||||
} catch (CancelledKeyException e) {
|
|
||||||
// Raised by accept() when the server socket was closed.
|
|
||||||
} catch (ClosedSelectorException e) {
|
|
||||||
// Raised by accept() when the server socket was closed.
|
|
||||||
} catch (ClosedChannelException e) {
|
|
||||||
// Closed as requested.
|
|
||||||
} catch (Throwable e) {
|
|
||||||
if (logger.isWarnEnabled()) {
|
|
||||||
logger.warn(
|
|
||||||
"Failed to accept a connection.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void processConnectTimeout(Set<SelectionKey> keys, long currentTimeNanos) {
|
protected void processConnectTimeout(Set<SelectionKey> keys, long currentTimeNanos) {
|
||||||
ConnectException cause = null;
|
ConnectException cause = null;
|
||||||
for (SelectionKey k: keys) {
|
for (SelectionKey k: keys) {
|
||||||
@ -389,38 +239,21 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// check if the channel is in
|
// check if the channel is in
|
||||||
Object attachment = k.attachment();
|
// FIXME: Implement connect timeout.
|
||||||
if (attachment instanceof NioClientSocketChannel) {
|
// Channel ch = (Channel) k.attachment();
|
||||||
NioClientSocketChannel ch = (NioClientSocketChannel) attachment;
|
// if (attachment instanceof NioClientSocketChannel) {
|
||||||
if (!ch.isConnected() && ch.connectDeadlineNanos > 0 && currentTimeNanos >= ch.connectDeadlineNanos) {
|
// NioClientSocketChannel ch = (NioClientSocketChannel) attachment;
|
||||||
|
// if (!ch.isConnected() && ch.connectDeadlineNanos > 0 && currentTimeNanos >= ch.connectDeadlineNanos) {
|
||||||
if (cause == null) {
|
//
|
||||||
cause = new ConnectException("connection timed out");
|
// if (cause == null) {
|
||||||
}
|
// cause = new ConnectException("connection timed out");
|
||||||
|
// }
|
||||||
ch.connectFuture.setFailure(cause);
|
//
|
||||||
fireExceptionCaught(ch, cause);
|
// ch.connectFuture.setFailure(cause);
|
||||||
ch.getWorker().close(ch, succeededFuture(ch));
|
// fireExceptionCaught(ch, cause);
|
||||||
}
|
// ch.getWorker().close(ch, succeededFuture(ch));
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void connect(SelectionKey k) {
|
|
||||||
final NioClientSocketChannel ch = (NioClientSocketChannel) k.attachment();
|
|
||||||
try {
|
|
||||||
// TODO: Remove cast
|
|
||||||
if (ch.getJdkChannel().finishConnect()) {
|
|
||||||
registerTask(ch, ch.connectFuture);
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
ch.connectFuture.setFailure(t);
|
|
||||||
fireExceptionCaught(ch, t);
|
|
||||||
k.cancel(); // Some JDK implementations run into an infinite loop without this.
|
|
||||||
ch.getWorker().close(ch, succeededFuture(ch));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,495 +265,4 @@ abstract class SelectorEventLoop extends SingleThreadEventLoop {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected void close(SelectionKey k) {
|
|
||||||
Object attachment = k.attachment();
|
|
||||||
if (attachment instanceof AbstractNioChannel) {
|
|
||||||
AbstractNioChannel ch = (AbstractNioChannel) attachment;
|
|
||||||
close(ch, succeededFuture(ch));
|
|
||||||
} else if (attachment instanceof NioServerSocketChannel) {
|
|
||||||
NioServerSocketChannel ch = (NioServerSocketChannel) attachment;
|
|
||||||
close(ch, succeededFuture(ch));
|
|
||||||
} else {
|
|
||||||
// TODO: What todo ?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeFromUserCode(final AbstractNioChannel channel) {
|
|
||||||
|
|
||||||
if (!channel.isConnected()) {
|
|
||||||
cleanUpWriteBuffer(channel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (scheduleWriteIfNecessary(channel)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From here, we are sure Thread.currentThread() == workerThread.
|
|
||||||
|
|
||||||
if (channel.writeSuspended) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel.inWriteNowLoop) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
write0(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeFromTaskLoop(AbstractNioChannel ch) {
|
|
||||||
if (!ch.writeSuspended) {
|
|
||||||
write0(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeFromSelectorLoop(final SelectionKey k) {
|
|
||||||
AbstractNioChannel ch = (AbstractNioChannel) k.attachment();
|
|
||||||
ch.writeSuspended = false;
|
|
||||||
write0(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected boolean scheduleWriteIfNecessary(final AbstractNioChannel channel) {
|
|
||||||
if (!inEventLoop()) {
|
|
||||||
if (channel.writeTaskInTaskQueue.compareAndSet(false, true)) {
|
|
||||||
execute(channel.writeTask);
|
|
||||||
}
|
|
||||||
|
|
||||||
final Selector workerSelector = selector;
|
|
||||||
if (workerSelector != null) {
|
|
||||||
if (wakenUp.compareAndSet(false, true)) {
|
|
||||||
workerSelector.wakeup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void write0(AbstractNioChannel channel) {
|
|
||||||
boolean open = true;
|
|
||||||
boolean addOpWrite = false;
|
|
||||||
boolean removeOpWrite = false;
|
|
||||||
boolean inEventLoop = inEventLoop();
|
|
||||||
|
|
||||||
long writtenBytes = 0;
|
|
||||||
|
|
||||||
final SendBufferPool sendBufferPool = this.sendBufferPool;
|
|
||||||
|
|
||||||
final WritableByteChannel ch = channel.getJdkChannel();
|
|
||||||
final Queue<MessageEvent> writeBuffer = channel.writeBufferQueue;
|
|
||||||
final int writeSpinCount = channel.getConfig().getWriteSpinCount();
|
|
||||||
synchronized (channel.writeLock) {
|
|
||||||
channel.inWriteNowLoop = true;
|
|
||||||
for (;;) {
|
|
||||||
MessageEvent evt = channel.currentWriteEvent;
|
|
||||||
SendBuffer buf;
|
|
||||||
if (evt == null) {
|
|
||||||
if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) {
|
|
||||||
removeOpWrite = true;
|
|
||||||
channel.writeSuspended = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage());
|
|
||||||
} else {
|
|
||||||
buf = channel.currentWriteBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelFuture future = evt.getFuture();
|
|
||||||
try {
|
|
||||||
long localWrittenBytes = 0;
|
|
||||||
for (int i = writeSpinCount; i > 0; i --) {
|
|
||||||
localWrittenBytes = buf.transferTo(ch);
|
|
||||||
if (localWrittenBytes != 0) {
|
|
||||||
writtenBytes += localWrittenBytes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (buf.finished()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buf.finished()) {
|
|
||||||
// Successful write - proceed to the next message.
|
|
||||||
buf.release();
|
|
||||||
channel.currentWriteEvent = null;
|
|
||||||
channel.currentWriteBuffer = null;
|
|
||||||
evt = null;
|
|
||||||
buf = null;
|
|
||||||
future.setSuccess();
|
|
||||||
} else {
|
|
||||||
// Not written fully - perhaps the kernel buffer is full.
|
|
||||||
addOpWrite = true;
|
|
||||||
channel.writeSuspended = true;
|
|
||||||
|
|
||||||
if (localWrittenBytes > 0) {
|
|
||||||
// Notify progress listeners if necessary.
|
|
||||||
future.setProgress(
|
|
||||||
localWrittenBytes,
|
|
||||||
buf.writtenBytes(), buf.totalBytes());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (AsynchronousCloseException e) {
|
|
||||||
// Doesn't need a user attention - ignore.
|
|
||||||
} catch (Throwable t) {
|
|
||||||
if (buf != null) {
|
|
||||||
buf.release();
|
|
||||||
}
|
|
||||||
channel.currentWriteEvent = null;
|
|
||||||
channel.currentWriteBuffer = null;
|
|
||||||
buf = null;
|
|
||||||
evt = null;
|
|
||||||
future.setFailure(t);
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} else {
|
|
||||||
fireExceptionCaughtLater(channel, t);
|
|
||||||
}
|
|
||||||
if (t instanceof IOException) {
|
|
||||||
open = false;
|
|
||||||
close(channel, succeededFuture(channel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channel.inWriteNowLoop = false;
|
|
||||||
|
|
||||||
// Initially, the following block was executed after releasing
|
|
||||||
// the writeLock, but there was a race condition, and it has to be
|
|
||||||
// executed before releasing the writeLock:
|
|
||||||
//
|
|
||||||
// https://issues.jboss.org/browse/NETTY-410
|
|
||||||
//
|
|
||||||
if (open) {
|
|
||||||
if (addOpWrite) {
|
|
||||||
setOpWrite(channel);
|
|
||||||
} else if (removeOpWrite) {
|
|
||||||
clearOpWrite(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireWriteComplete(channel, writtenBytes);
|
|
||||||
} else {
|
|
||||||
fireWriteCompleteLater(channel, writtenBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setOpWrite(AbstractNioChannel channel) {
|
|
||||||
Selector selector = this.selector;
|
|
||||||
SelectionKey key = channel.getJdkChannel().keyFor(selector);
|
|
||||||
if (key == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!key.isValid()) {
|
|
||||||
close(key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// interestOps can change at any time and at any thread.
|
|
||||||
// Acquire a lock to avoid possible race condition.
|
|
||||||
synchronized (channel.interestOpsLock) {
|
|
||||||
int interestOps = channel.getRawInterestOps();
|
|
||||||
if ((interestOps & SelectionKey.OP_WRITE) == 0) {
|
|
||||||
interestOps |= SelectionKey.OP_WRITE;
|
|
||||||
key.interestOps(interestOps);
|
|
||||||
channel.setRawInterestOpsNow(interestOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void clearOpWrite(AbstractNioChannel channel) {
|
|
||||||
Selector selector = this.selector;
|
|
||||||
SelectionKey key = channel.getJdkChannel().keyFor(selector);
|
|
||||||
if (key == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!key.isValid()) {
|
|
||||||
close(key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// interestOps can change at any time and at any thread.
|
|
||||||
// Acquire a lock to avoid possible race condition.
|
|
||||||
synchronized (channel.interestOpsLock) {
|
|
||||||
int interestOps = channel.getRawInterestOps();
|
|
||||||
if ((interestOps & SelectionKey.OP_WRITE) != 0) {
|
|
||||||
interestOps &= ~SelectionKey.OP_WRITE;
|
|
||||||
key.interestOps(interestOps);
|
|
||||||
channel.setRawInterestOpsNow(interestOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void close(NioServerSocketChannel channel, ChannelFuture future) {
|
|
||||||
boolean inEventLoop = inEventLoop();
|
|
||||||
|
|
||||||
boolean bound = channel.isBound();
|
|
||||||
try {
|
|
||||||
if (channel.socket.isOpen()) {
|
|
||||||
channel.socket.close();
|
|
||||||
if (selector != null) {
|
|
||||||
selector.wakeup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the boss thread is not running so that that the future
|
|
||||||
// is notified after a new connection cannot be accepted anymore.
|
|
||||||
// See NETTY-256 for more information.
|
|
||||||
channel.shutdownLock.lock();
|
|
||||||
try {
|
|
||||||
if (channel.setClosed()) {
|
|
||||||
future.setSuccess();
|
|
||||||
if (bound) {
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireChannelUnbound(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelUnboundLater(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireChannelClosed(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelClosedLater(channel);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
future.setSuccess();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
channel.shutdownLock.unlock();
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} else {
|
|
||||||
fireExceptionCaughtLater(channel, t);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close(AbstractNioChannel channel, ChannelFuture future) {
|
|
||||||
boolean connected = channel.isConnected();
|
|
||||||
boolean bound = channel.isBound();
|
|
||||||
boolean inEventLoop = inEventLoop();
|
|
||||||
|
|
||||||
try {
|
|
||||||
channel.getJdkChannel().close();
|
|
||||||
cancelledKeys ++;
|
|
||||||
|
|
||||||
if (channel.setClosed()) {
|
|
||||||
future.setSuccess();
|
|
||||||
if (connected) {
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireChannelDisconnected(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelDisconnectedLater(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bound) {
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireChannelUnbound(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelUnboundLater(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanUpWriteBuffer(channel);
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireChannelClosed(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelClosedLater(channel);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
future.setSuccess();
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} else {
|
|
||||||
fireExceptionCaughtLater(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void cleanUpWriteBuffer(AbstractNioChannel channel) {
|
|
||||||
Exception cause = null;
|
|
||||||
boolean fireExceptionCaught = false;
|
|
||||||
|
|
||||||
// Clean up the stale messages in the write buffer.
|
|
||||||
synchronized (channel.writeLock) {
|
|
||||||
MessageEvent evt = channel.currentWriteEvent;
|
|
||||||
if (evt != null) {
|
|
||||||
// Create the exception only once to avoid the excessive overhead
|
|
||||||
// caused by fillStackTrace.
|
|
||||||
if (channel.isOpen()) {
|
|
||||||
cause = new NotYetConnectedException();
|
|
||||||
} else {
|
|
||||||
cause = new ClosedChannelException();
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelFuture future = evt.getFuture();
|
|
||||||
channel.currentWriteBuffer.release();
|
|
||||||
channel.currentWriteBuffer = null;
|
|
||||||
channel.currentWriteEvent = null;
|
|
||||||
evt = null;
|
|
||||||
future.setFailure(cause);
|
|
||||||
fireExceptionCaught = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Queue<MessageEvent> writeBuffer = channel.writeBufferQueue;
|
|
||||||
if (!writeBuffer.isEmpty()) {
|
|
||||||
// Create the exception only once to avoid the excessive overhead
|
|
||||||
// caused by fillStackTrace.
|
|
||||||
if (cause == null) {
|
|
||||||
if (channel.isOpen()) {
|
|
||||||
cause = new NotYetConnectedException();
|
|
||||||
} else {
|
|
||||||
cause = new ClosedChannelException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
evt = writeBuffer.poll();
|
|
||||||
if (evt == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
evt.getFuture().setFailure(cause);
|
|
||||||
fireExceptionCaught = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fireExceptionCaught) {
|
|
||||||
if (inEventLoop()) {
|
|
||||||
fireExceptionCaught(channel, cause);
|
|
||||||
} else {
|
|
||||||
fireExceptionCaughtLater(channel, cause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInterestOps(AbstractNioChannel channel, ChannelFuture future, int interestOps) {
|
|
||||||
boolean changed = false;
|
|
||||||
boolean inEventLoop = inEventLoop();
|
|
||||||
try {
|
|
||||||
// interestOps can change at any time and at any thread.
|
|
||||||
// Acquire a lock to avoid possible race condition.
|
|
||||||
synchronized (channel.interestOpsLock) {
|
|
||||||
Selector selector = this.selector;
|
|
||||||
SelectionKey key = channel.getJdkChannel().keyFor(selector);
|
|
||||||
|
|
||||||
// Override OP_WRITE flag - a user cannot change this flag.
|
|
||||||
interestOps &= ~Channel.OP_WRITE;
|
|
||||||
interestOps |= channel.getRawInterestOps() & Channel.OP_WRITE;
|
|
||||||
|
|
||||||
if (key == null || selector == null) {
|
|
||||||
if (channel.getRawInterestOps() != interestOps) {
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not registered to the worker yet.
|
|
||||||
// Set the rawInterestOps immediately; RegisterTask will pick it up.
|
|
||||||
channel.setRawInterestOpsNow(interestOps);
|
|
||||||
|
|
||||||
future.setSuccess();
|
|
||||||
if (changed) {
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireChannelInterestChanged(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelInterestChangedLater(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (CONSTRAINT_LEVEL) {
|
|
||||||
case 0:
|
|
||||||
if (channel.getRawInterestOps() != interestOps) {
|
|
||||||
key.interestOps(interestOps);
|
|
||||||
if (!inEventLoop &&
|
|
||||||
wakenUp.compareAndSet(false, true)) {
|
|
||||||
selector.wakeup();
|
|
||||||
}
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
if (channel.getRawInterestOps() != interestOps) {
|
|
||||||
if (inEventLoop) {
|
|
||||||
key.interestOps(interestOps);
|
|
||||||
changed = true;
|
|
||||||
} else {
|
|
||||||
selectorGuard.readLock().lock();
|
|
||||||
try {
|
|
||||||
if (wakenUp.compareAndSet(false, true)) {
|
|
||||||
selector.wakeup();
|
|
||||||
}
|
|
||||||
key.interestOps(interestOps);
|
|
||||||
changed = true;
|
|
||||||
} finally {
|
|
||||||
selectorGuard.readLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
channel.setRawInterestOpsNow(interestOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
future.setSuccess();
|
|
||||||
if (changed) {
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireChannelInterestChanged(channel);
|
|
||||||
} else {
|
|
||||||
fireChannelInterestChangedLater(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (CancelledKeyException e) {
|
|
||||||
// setInterestOps() was called on a closed channel.
|
|
||||||
ClosedChannelException cce = new ClosedChannelException();
|
|
||||||
future.setFailure(cce);
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireExceptionCaught(channel, cce);
|
|
||||||
} else {
|
|
||||||
fireExceptionCaughtLater(channel, cce);
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
future.setFailure(t);
|
|
||||||
if (inEventLoop) {
|
|
||||||
fireExceptionCaught(channel, t);
|
|
||||||
} else {
|
|
||||||
fireExceptionCaughtLater(channel, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read is called when a Selector has been notified that the underlying channel
|
|
||||||
* was something to be read. The channel would previously have registered its interest
|
|
||||||
* in read operations.
|
|
||||||
*
|
|
||||||
* @param k The selection key which contains the Selector registration information.
|
|
||||||
*/
|
|
||||||
protected abstract boolean read(SelectionKey k);
|
|
||||||
|
|
||||||
protected abstract void registerTask(AbstractNioChannel channel, ChannelFuture future);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user