All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.netty.util.concurrent.AbstractScheduledEventExecutor Maven / Gradle / Ivy

There is a newer version: 2.38.0
Show newest version
/*
 * Copyright 2015 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:
 *
 *   https://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.util.concurrent;

import io.netty.util.internal.DefaultPriorityQueue;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PriorityQueue;

import java.util.Comparator;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * Abstract base class for {@link EventExecutor}s that want to support scheduling.
 */
public abstract class AbstractScheduledEventExecutor extends AbstractEventExecutor {
    private static final Comparator> SCHEDULED_FUTURE_TASK_COMPARATOR =
            new Comparator>() {
                @Override
                public int compare(ScheduledFutureTask o1, ScheduledFutureTask o2) {
                    return o1.compareTo(o2);
                }
            };

    private static final long START_TIME = System.nanoTime();

    static final Runnable WAKEUP_TASK = new Runnable() {
       @Override
       public void run() { } // Do nothing
    };

    PriorityQueue> scheduledTaskQueue;

    long nextTaskId;

    protected AbstractScheduledEventExecutor() {
    }

    protected AbstractScheduledEventExecutor(EventExecutorGroup parent) {
        super(parent);
    }

    /**
     * Get the current time in nanoseconds by this executor's clock. This is not the same as {@link System#nanoTime()}
     * for two reasons:
     *
     * 
    *
  • We apply a fixed offset to the {@link System#nanoTime() nanoTime}
  • *
  • Implementations (in particular EmbeddedEventLoop) may use their own time source so they can control time * for testing purposes.
  • *
*/ protected long getCurrentTimeNanos() { return defaultCurrentTimeNanos(); } /** * @deprecated Use the non-static {@link #getCurrentTimeNanos()} instead. */ @Deprecated protected static long nanoTime() { return defaultCurrentTimeNanos(); } static long defaultCurrentTimeNanos() { return System.nanoTime() - START_TIME; } static long deadlineNanos(long nanoTime, long delay) { long deadlineNanos = nanoTime + delay; // Guard against overflow return deadlineNanos < 0 ? Long.MAX_VALUE : deadlineNanos; } /** * Given an arbitrary deadline {@code deadlineNanos}, calculate the number of nano seconds from now * {@code deadlineNanos} would expire. * @param deadlineNanos An arbitrary deadline in nano seconds. * @return the number of nano seconds from now {@code deadlineNanos} would expire. */ protected static long deadlineToDelayNanos(long deadlineNanos) { return ScheduledFutureTask.deadlineToDelayNanos(defaultCurrentTimeNanos(), deadlineNanos); } /** * The initial value used for delay and computations based upon a monatomic time source. * @return initial value used for delay and computations based upon a monatomic time source. */ protected static long initialNanoTime() { return START_TIME; } PriorityQueue> scheduledTaskQueue() { if (scheduledTaskQueue == null) { scheduledTaskQueue = new DefaultPriorityQueue>( SCHEDULED_FUTURE_TASK_COMPARATOR, // Use same initial capacity as java.util.PriorityQueue 11); } return scheduledTaskQueue; } private static boolean isNullOrEmpty(Queue> queue) { return queue == null || queue.isEmpty(); } /** * Cancel all scheduled tasks. * * This method MUST be called only when {@link #inEventLoop()} is {@code true}. */ protected void cancelScheduledTasks() { assert inEventLoop(); PriorityQueue> scheduledTaskQueue = this.scheduledTaskQueue; if (isNullOrEmpty(scheduledTaskQueue)) { return; } final ScheduledFutureTask[] scheduledTasks = scheduledTaskQueue.toArray(new ScheduledFutureTask[0]); for (ScheduledFutureTask task: scheduledTasks) { task.cancelWithoutRemove(false); } scheduledTaskQueue.clearIgnoringIndexes(); } /** * @see #pollScheduledTask(long) */ protected final Runnable pollScheduledTask() { return pollScheduledTask(getCurrentTimeNanos()); } /** * Return the {@link Runnable} which is ready to be executed with the given {@code nanoTime}. * You should use {@link #getCurrentTimeNanos()} to retrieve the correct {@code nanoTime}. */ protected final Runnable pollScheduledTask(long nanoTime) { assert inEventLoop(); ScheduledFutureTask scheduledTask = peekScheduledTask(); if (scheduledTask == null || scheduledTask.deadlineNanos() - nanoTime > 0) { return null; } scheduledTaskQueue.remove(); scheduledTask.setConsumed(); return scheduledTask; } /** * Return the nanoseconds until the next scheduled task is ready to be run or {@code -1} if no task is scheduled. */ protected final long nextScheduledTaskNano() { ScheduledFutureTask scheduledTask = peekScheduledTask(); return scheduledTask != null ? scheduledTask.delayNanos() : -1; } /** * Return the deadline (in nanoseconds) when the next scheduled task is ready to be run or {@code -1} * if no task is scheduled. */ protected final long nextScheduledTaskDeadlineNanos() { ScheduledFutureTask scheduledTask = peekScheduledTask(); return scheduledTask != null ? scheduledTask.deadlineNanos() : -1; } final ScheduledFutureTask peekScheduledTask() { Queue> scheduledTaskQueue = this.scheduledTaskQueue; return scheduledTaskQueue != null ? scheduledTaskQueue.peek() : null; } /** * Returns {@code true} if a scheduled task is ready for processing. */ protected final boolean hasScheduledTasks() { ScheduledFutureTask scheduledTask = peekScheduledTask(); return scheduledTask != null && scheduledTask.deadlineNanos() <= getCurrentTimeNanos(); } @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { ObjectUtil.checkNotNull(command, "command"); ObjectUtil.checkNotNull(unit, "unit"); if (delay < 0) { delay = 0; } validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask( this, command, deadlineNanos(getCurrentTimeNanos(), unit.toNanos(delay)))); } @Override public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { ObjectUtil.checkNotNull(callable, "callable"); ObjectUtil.checkNotNull(unit, "unit"); if (delay < 0) { delay = 0; } validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask( this, callable, deadlineNanos(getCurrentTimeNanos(), unit.toNanos(delay)))); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { ObjectUtil.checkNotNull(command, "command"); ObjectUtil.checkNotNull(unit, "unit"); if (initialDelay < 0) { throw new IllegalArgumentException( String.format("initialDelay: %d (expected: >= 0)", initialDelay)); } if (period <= 0) { throw new IllegalArgumentException( String.format("period: %d (expected: > 0)", period)); } validateScheduled0(initialDelay, unit); validateScheduled0(period, unit); return schedule(new ScheduledFutureTask( this, command, deadlineNanos(getCurrentTimeNanos(), unit.toNanos(initialDelay)), unit.toNanos(period))); } @Override public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { ObjectUtil.checkNotNull(command, "command"); ObjectUtil.checkNotNull(unit, "unit"); if (initialDelay < 0) { throw new IllegalArgumentException( String.format("initialDelay: %d (expected: >= 0)", initialDelay)); } if (delay <= 0) { throw new IllegalArgumentException( String.format("delay: %d (expected: > 0)", delay)); } validateScheduled0(initialDelay, unit); validateScheduled0(delay, unit); return schedule(new ScheduledFutureTask( this, command, deadlineNanos(getCurrentTimeNanos(), unit.toNanos(initialDelay)), -unit.toNanos(delay))); } @SuppressWarnings("deprecation") private void validateScheduled0(long amount, TimeUnit unit) { validateScheduled(amount, unit); } /** * Sub-classes may override this to restrict the maximal amount of time someone can use to schedule a task. * * @deprecated will be removed in the future. */ @Deprecated protected void validateScheduled(long amount, TimeUnit unit) { // NOOP } final void scheduleFromEventLoop(final ScheduledFutureTask task) { // nextTaskId a long and so there is no chance it will overflow back to 0 scheduledTaskQueue().add(task.setId(++nextTaskId)); } private ScheduledFuture schedule(final ScheduledFutureTask task) { if (inEventLoop()) { scheduleFromEventLoop(task); } else { final long deadlineNanos = task.deadlineNanos(); // task will add itself to scheduled task queue when run if not expired if (beforeScheduledTaskSubmitted(deadlineNanos)) { execute(task); } else { lazyExecute(task); // Second hook after scheduling to facilitate race-avoidance if (afterScheduledTaskSubmitted(deadlineNanos)) { execute(WAKEUP_TASK); } } } return task; } final void removeScheduled(final ScheduledFutureTask task) { assert task.isCancelled(); if (inEventLoop()) { scheduledTaskQueue().removeTyped(task); } else { // task will remove itself from scheduled task queue when it runs lazyExecute(task); } } /** * Called from arbitrary non-{@link EventExecutor} threads prior to scheduled task submission. * Returns {@code true} if the {@link EventExecutor} thread should be woken immediately to * process the scheduled task (if not already awake). *

* If {@code false} is returned, {@link #afterScheduledTaskSubmitted(long)} will be called with * the same value after the scheduled task is enqueued, providing another opportunity * to wake the {@link EventExecutor} thread if required. * * @param deadlineNanos deadline of the to-be-scheduled task * relative to {@link AbstractScheduledEventExecutor#getCurrentTimeNanos()} * @return {@code true} if the {@link EventExecutor} thread should be woken, {@code false} otherwise */ protected boolean beforeScheduledTaskSubmitted(long deadlineNanos) { return true; } /** * See {@link #beforeScheduledTaskSubmitted(long)}. Called only after that method returns false. * * @param deadlineNanos relative to {@link AbstractScheduledEventExecutor#getCurrentTimeNanos()} * @return {@code true} if the {@link EventExecutor} thread should be woken, {@code false} otherwise */ protected boolean afterScheduledTaskSubmitted(long deadlineNanos) { return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy