/* * 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; } }