/*
* Copyright 2012 The Netty Project
* Copyright 2013 Red Hat, Inc.
*
* 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 org.fusesource.hawtdispatch.netty;
import io.netty.channel.*;
import org.fusesource.hawtdispatch.Dispatch;
import org.fusesource.hawtdispatch.DispatchPriority;
import org.fusesource.hawtdispatch.DispatchQueue;
import org.fusesource.hawtdispatch.internal.ThreadDispatchQueue;
import java.util.*;
import java.util.concurrent.*;
/**
* {@link EventLoop} implementations which will
* handle HawtDispatch based {@link Channel}s.
*
* @author <a href="mailto:nmaurer@redhat.com">Norman Maurer</a>
*/
final class HawtEventLoop extends AbstractExecutorService implements EventLoop {
private final EventLoopGroup parent;
private volatile boolean shutdown;
private final DispatchQueue queue;
HawtEventLoop(EventLoopGroup parent, DispatchQueue queue) {
if (parent == null) {
throw new NullPointerException("parent");
}
if (queue == null) {
throw new NullPointerException("queue");
}
this.parent = parent;
this.queue = queue;
}
/**
* Returns the backing {@link DispatchQueue}
*/
DispatchQueue queue() {
return queue;
}
@Override
public EventLoopGroup parent() {
return parent;
}
@Override
public boolean inEventLoop() {
return queue.isExecuting();
}
@Override
public EventLoop next() {
return this;
}
@Override
public void shutdown() {
shutdown = true;
}
@Override
public List<Runnable> shutdownNow() {
shutdown = true;
return Collections.emptyList();
}
@Override
public boolean isShutdown() {
return shutdown;
}
@Override
public boolean isTerminated() {
return shutdown;
}
@Override
public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException {
return shutdown;
}
@SuppressWarnings("unchecked")
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> vCallable, long delay, TimeUnit timeUnit) {
return new ScheduledFutureTask(vCallable, timeUnit.toNanos(delay)).schedule();
}
@Override
public ScheduledFuture<?> schedule(final Runnable runnable, long delay, TimeUnit timeUnit) {
return new ScheduledFutureTask(runnable, timeUnit.toNanos(delay)).schedule();
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, long delay, long period, TimeUnit timeUnit) {
return new ScheduledFutureTask(runnable, timeUnit.toNanos(delay), timeUnit.toNanos(period)).schedule();
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable, long initialDelay, long delay, TimeUnit timeUnit) {
throw new UnsupportedOperationException();
}
private final class ScheduledFutureTask<V> extends FutureTask<V> implements ScheduledFuture<V> {
private long deadlineNanos;
private final long periodNanos;
ScheduledFutureTask(Runnable runnable,long nanoTime) {
super(runnable, null);
deadlineNanos = System.nanoTime()+nanoTime;
periodNanos = 0;
}
ScheduledFutureTask(Runnable runnable, long nanoTime, long period) {
super(runnable, null);
if (period == 0) {
throw new IllegalArgumentException("period: 0 (expected: != 0)");
}
deadlineNanos = nanoTime;
periodNanos = period;
}
ScheduledFutureTask(Callable<V> callable, long nanoTime) {
super(callable);
deadlineNanos = nanoTime;
periodNanos = 0;
}
public long delayNanos() {
return Math.max(0, deadlineNanos - System.nanoTime());
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(delayNanos(), TimeUnit.NANOSECONDS);
}
@Override
public int compareTo(Delayed o) {
if (this == o) {
return 0;
}
ScheduledFutureTask<?> that = (ScheduledFutureTask<?>) o;
long d = deadlineNanos - that.deadlineNanos;
if (d < 0) {
return -1;
} else if (d > 0) {
return 1;
} else {
return 1;
}
}
@Override
public void run() {
if (periodNanos == 0) {
super.run();
} else {
boolean reset = runAndReset();
if (reset && !isShutdown()) {
long p = periodNanos;
if (p > 0) {
deadlineNanos += p;
} else {
deadlineNanos = System.nanoTime() - p;
}
schedule();
}
}
}
ScheduledFuture<V> schedule() {
queue.executeAfter(delayNanos(), TimeUnit.NANOSECONDS, this);
return this;
}
}
@Override
public void execute(Runnable runnable) {
queue.execute(runnable);
}
@Override
public boolean inEventLoop(Thread thread) {
for (DispatchQueue queue: Dispatch.getThreadQueues(DispatchPriority.DEFAULT)) {
if (thread == ((ThreadDispatchQueue) queue).getThread()) {
return true;
}
}
return false;
}
@Override
public ChannelFuture register(Channel channel) {
if (channel == null) {
throw new NullPointerException("channel");
}
return register(channel, channel.newPromise());
}
@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
if (isShutdown()) {
channel.unsafe().closeForcibly();
promise.setFailure(new EventLoopException("cannot register a channel to a shut down loop"));
return promise;
}
if (inEventLoop()) {
channel.unsafe().register(this, promise);
} else {
execute(new Runnable() {
@Override
public void run() {
channel.unsafe().register(HawtEventLoop.this, promise);
}
});
}
return promise;
}
}