2012-06-04 22:31:44 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2012 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.
|
|
|
|
*/
|
2012-04-03 15:03:04 +02:00
|
|
|
package io.netty.channel;
|
|
|
|
|
2013-03-05 21:41:19 +01:00
|
|
|
import io.netty.util.concurrent.SingleThreadEventExecutor;
|
|
|
|
|
2013-08-20 18:42:56 +02:00
|
|
|
import java.util.concurrent.Executor;
|
2012-04-03 15:03:04 +02:00
|
|
|
import java.util.concurrent.ThreadFactory;
|
2012-05-11 13:19:57 +02:00
|
|
|
|
2012-12-18 07:23:42 +01:00
|
|
|
/**
|
2014-03-24 03:05:51 +01:00
|
|
|
* Abstract base class for {@link EventLoop}s that execute all its submitted tasks in a single thread.
|
2012-12-18 07:23:42 +01:00
|
|
|
*
|
|
|
|
*/
|
2012-06-02 02:51:19 +02:00
|
|
|
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
|
2012-04-29 11:40:55 +02:00
|
|
|
|
2014-03-24 10:09:27 +01:00
|
|
|
private final ChannelHandlerInvoker invoker = new DefaultChannelHandlerInvoker(this);
|
|
|
|
|
2013-04-27 09:39:19 +02:00
|
|
|
protected SingleThreadEventLoop(EventLoopGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp) {
|
|
|
|
super(parent, threadFactory, addTaskWakesUp);
|
2012-08-10 13:17:18 +02:00
|
|
|
}
|
|
|
|
|
2013-08-20 18:42:56 +02:00
|
|
|
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor, boolean addTaskWakesUp) {
|
|
|
|
super(parent, executor, addTaskWakesUp);
|
|
|
|
}
|
|
|
|
|
2012-08-10 13:17:18 +02:00
|
|
|
@Override
|
|
|
|
public EventLoopGroup parent() {
|
|
|
|
return (EventLoopGroup) super.parent();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public EventLoop next() {
|
|
|
|
return (EventLoop) super.next();
|
2012-04-03 15:03:04 +02:00
|
|
|
}
|
|
|
|
|
2014-03-24 10:09:27 +01:00
|
|
|
@Override
|
|
|
|
public ChannelHandlerInvoker asInvoker() {
|
|
|
|
return invoker;
|
|
|
|
}
|
|
|
|
|
2012-04-29 10:59:42 +02:00
|
|
|
@Override
|
2012-04-03 15:19:35 +02:00
|
|
|
public ChannelFuture register(Channel channel) {
|
2013-12-21 10:08:58 +01:00
|
|
|
return register(channel, new DefaultChannelPromise(channel, this));
|
2012-04-03 15:03:04 +02:00
|
|
|
}
|
|
|
|
|
2012-04-29 11:40:55 +02:00
|
|
|
@Override
|
2012-12-30 17:40:24 +01:00
|
|
|
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
|
2013-03-21 09:48:10 +01:00
|
|
|
if (channel == null) {
|
|
|
|
throw new NullPointerException("channel");
|
Reduce the chance of RejectedExecutionException
When a Netty application shuts down, a user often sees a REE
(RejectedExecutionException).
A REE is raised due to various reasons we don't have control over, such
as:
- A client connects to a server while the server is shutting down.
- An event is triggered for a closed Channel while its event loop is
also shutting down. Some of them are:
- channelDeregistered (triggered after a channel is closed)
- freeIn/OutboundBuffer (triggered after channelDeregistered)
- userEventTriggered (triggered anytime)
To address this issue, a new method called confirmShutdown() has been
added to SingleThreadEventExecutor. After a user calls shutdown(),
confirmShutdown() runs any remaining tasks in the task queue and ensures
no events are triggered for last 2 seconds. If any task are added to
the task queue before 2 seconds passes, confirmShutdown() prevents the
event loop from terminating by returning false.
Now that SingleThreadEventExecutor needs to accept tasks even after
shutdown(), its execute() method only rejects the task after the event
loop is terminated (i.e. isTerminated() returns true.) Except that,
there's no change in semantics.
SingleThreadEventExecutor also checks if its subclass called
confirmShutdown() in its run() implementation, so that Netty developers
can make sure they shut down their event loop impementation correctly.
It also fixes a bug in AioSocketChannel, revealed by delayed shutdown,
where an inboundBufferUpdated() event is triggered on a closed Channel
with deallocated buffers.
Caveats:
Because SingleThreadEventExecutor.takeTask() does not have a notion of
timeout, confirmShutdown() adds a dummy task (WAKEUP_TASK) to wake up
takeTask() immediately and instead sleeps hard-coded 100ms. I'll
address this issue later by modifying takeTask() times out dynamically.
Miscellaneous changes:
SingleThreadEventExecutor.wakeup() now has the default implementation.
Instead of interrupting the current thread, it simply adds a dummy task
(WAKEUP_TASK) to the task queue, which is more elegant and efficient.
NioEventLoop is the only implementation that overrides it. All other
implementations' wakeup()s were removed thanks to this change.
2012-11-22 12:45:49 +01:00
|
|
|
}
|
2013-03-21 09:48:10 +01:00
|
|
|
if (promise == null) {
|
|
|
|
throw new NullPointerException("promise");
|
2012-05-02 08:01:58 +02:00
|
|
|
}
|
2012-11-30 07:07:26 +01:00
|
|
|
|
2013-03-21 09:48:10 +01:00
|
|
|
channel.unsafe().register(this, promise);
|
2012-12-30 17:40:24 +01:00
|
|
|
return promise;
|
2012-04-29 11:40:55 +02:00
|
|
|
}
|
2014-01-30 20:02:15 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected boolean wakesUpForTask(Runnable task) {
|
|
|
|
return !(task instanceof NonWakeupRunnable);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-24 03:05:51 +01:00
|
|
|
* Marker interface for {@link Runnable} that will not trigger an {@link #wakeup(boolean)} in all cases.
|
2014-01-30 20:02:15 +01:00
|
|
|
*/
|
|
|
|
interface NonWakeupRunnable extends Runnable { }
|
2012-04-03 15:03:04 +02:00
|
|
|
}
|