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

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

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
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