/*
* Copyright 2016 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.scheduler;
import ccre.channel.CancelOutput;
import ccre.channel.EventOutput;
import ccre.time.Time;
import ccre.verifier.FlowPhase;
import ccre.verifier.SetupPhase;
/**
* The main interface to the CCRE's scheduling thread. You can use this to
* register events to occur once or multiple times. A FIFO scheduler is used
* with no priorities. Specifically, the most proximal job will be executed, at
* a nanosecond granularity, and if multiple jobs are scheduled at the same
* time, the order is undefined.
*
* Jobs can run at any time after they are scheduled for - there are no
* guarantees provided, except that any job will run before any job scheduled
* for a later time.
*
* General concepts: there are both <code>schedule[...]Nanos</code> and
* <code>schedule[...]At</code> methods of various types: <code>Nanos</code>
* means that it will occur a specified amount of time after the invocation, and
* <code>At</code> means that the time to occur after will be specified, in the
* range returned by {@link Time#currentTimeNanos()}.
*
* The schedule methods also take scheduler tags, which are strings (hopefully
* constant to avoid reallocation issues) that are used for scheduler debugging,
* and are required for every schedule performed.
*
* Scheduled jobs must be provided as {@link EventOutput}s, and their
* {@link EventOutput#event()} method will be called to perform the job.
*
* @author skeggsc
*/
public class Scheduler {
// never modified except in unit tests - treat as if it were final
private static FullLoop mainloop = new FullLoop();
static {
mainloop.start();
}
/**
* Schedule an event to occur at a certain number of nanoseconds in the
* future. The current time for the purpose of the target time is chosen at
* an unspecified point during the execution of this method, usually as
* early as possible.
*
* @param tag the scheduler tag.
* @param nanos the number of nanoseconds in the future to schedule this
* event at.
* @param o the event to fire.
*/
@FlowPhase
public static void scheduleNanos(String tag, long nanos, EventOutput o) {
mainloop.scheduleOnce(tag, Time.currentTimeNanos() + nanos, o);
}
/**
* Schedule an event to occur at a certain nanosecond index, based on
* {@link Time#currentTimeNanos()}.
*
* @param tag the scheduler tag.
* @param nanoAt the time at which to run the event.
* @param o the event to fire.
*/
@FlowPhase
public static void scheduleAt(String tag, long nanoAt, EventOutput o) {
mainloop.scheduleOnce(tag, nanoAt, o);
}
/**
* Schedule an event to occur at a certain number of nanoseconds in the
* future. The current time for the purpose of the target time is chosen at
* an unspecified point during the execution of this method, usually as
* early as possible.
*
* The event can be cancelled before it occurs by calling
* {@link CancelOutput#cancel()} on the returned {@link CancelOutput}.
*
* @param tag the scheduler tag.
* @param nanos the number of nanoseconds in the future to schedule this
* event at.
* @param o the event to fire.
* @return the {@link CancelOutput} that can cancel the event.
*/
@FlowPhase
@Deprecated
public static CancelOutput scheduleCancellableNanos(String tag, long nanos, EventOutput o) {
return mainloop.scheduleCancellableOnce(tag, Time.currentTimeNanos() + nanos, o)::event;
}
/**
* Schedule an event to occur at a certain number of nanoseconds in the
* future. The current time for the purpose of the target time is chosen at
* an unspecified point during the execution of this method, usually as
* early as possible.
*
* The event can be interrupted before it occurs by firing the returned
* {@link EventOutput}.
*
* @param tag the scheduler tag.
* @param nanos the number of nanoseconds in the future to schedule this
* event at.
* @param o the event to fire.
* @return the {@link EventOutput} that can interrupt the event.
*/
@FlowPhase
public static EventOutput scheduleInterruptibleNanos(String tag, long nanos, EventOutput o) {
return mainloop.scheduleCancellableOnce(tag, Time.currentTimeNanos() + nanos, o);
}
/**
* Schedule an event to occur at a certain nanosecond index, based on
* {@link Time#currentTimeNanos()}.
*
* The event can be cancelled before it occurs by calling
* {@link CancelOutput#cancel()} on the returned {@link CancelOutput}.
*
* @param tag the scheduler tag.
* @param nanoAt the time at which to run the event.
* @param o the event to fire.
* @return the {@link CancelOutput} that can cancel the event.
*/
@Deprecated
@FlowPhase
public static CancelOutput scheduleCancellableAt(String tag, long nanoAt, EventOutput o) {
return mainloop.scheduleCancellableOnce(tag, nanoAt, o)::event;
}
/**
* Schedule an event to occur at a certain nanosecond index, based on
* {@link Time#currentTimeNanos()}.
*
* The event can be interrupted before it occurs by firing the returned
* {@link EventOutput}.
*
* @param tag the scheduler tag.
* @param nanoAt the time at which to run the event.
* @param o the event to fire.
* @return the {@link EventOutput} that can interrupt the event.
*/
@FlowPhase
public static EventOutput scheduleInterruptibleAt(String tag, long nanoAt, EventOutput o) {
return mainloop.scheduleCancellableOnce(tag, nanoAt, o);
}
/**
* Schedule an event to occur every <code>nanos</code> nanoseconds until
* cancelled by calling {@link CancelOutput#cancel()} on the returned
* {@link CancelOutput}.
*
* Unlike {@link #scheduleFixedRateNanos(String, long, EventOutput)},
* scheduling for subsequent events will be based on when the schedule
* actually occurs, not when it would in theory have occurred. This is so
* that, under load, this event won't bog down the system badly, but it
* increases jitter.
*
* @param tag the scheduler tag.
* @param nanos the number of nanoseconds between scheduled events.
* @param o the event to fire.
* @return the {@link CancelOutput} that can cancel the event.
*/
@SetupPhase
public static CancelOutput schedulePeriodicNanos(String tag, long nanos, EventOutput o) {
return mainloop.scheduleVariableRate(tag, nanos, o);
}
/**
* Schedule an event to occur every <code>nanos</code> nanoseconds until
* cancelled by calling {@link CancelOutput#cancel()} on the returned
* {@link CancelOutput}.
*
* Unlike {@link #schedulePeriodicNanos(String, long, EventOutput)},
* scheduling for subsequent events will be based on when the event was
* supposed to run, not when it actually ran. This is so that, overall, the
* rate will be consistent, even across load spikes.
*
* @param tag the scheduler tag.
* @param nanos the number of nanoseconds between scheduled events.
* @param o the event to fire.
* @return the {@link CancelOutput} that can cancel the event.
*/
@SetupPhase
public static CancelOutput scheduleFixedRateNanos(String tag, long nanos, EventOutput o) {
return mainloop.scheduleFixedRate(tag, Time.currentTimeNanos() + nanos, nanos, false, o);
}
// only used in unit tests
static synchronized void __UNSAFE_reset(IRunLoop loop) {
// unsafe b/c it cancels anything currently scheduled and it's sketchy
mainloop.terminate();
mainloop = new FullLoop(loop);
mainloop.start();
}
}