/** * Copyright 2008 Google Inc. * * Licensed 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.waveprotocol.wave.client.scheduler; /** * Unified interface for scheduling asynchronous work. * * This interface provides a relatively compact set of methods and * sub-interfaces for dealing with the varying task scheduling concerns of the * single-threaded web client. The central idea is that scheduling the same job * more than once (where "same" is defined by object identity) overrides the * previous scheduling of that same job. * * Contract: * <ul> * <li>The exact order of execution of jobs with equal priorities and delays is * undefined</li> * <li>The semantics of scheduling a job with any of the schedule variant * methods below are identical to doing that but also calling cancel on it * first. For example, this means that the delay timer is reset, and the * priority is changed to the most recent value.</li> * <li>Scheduling a job is the same as scheduling a delayed job with a delay of * zero</li> * <li>Scheduling a repeating process with a positive delay but zero interval * is the same as scheduling a delayed process</li> * <li>Scheduling an incremental task is the same as scheduling a repeating * incremental task with a delay and interval both of zero</li> * <li>If the interval of a repeating job is too small and enough happens * before it is next up for being run so that it "deserves" to be run more than * once, the additional runs are dropped. They are not queued up or "owed" to it * in any way.</li> * </ul> * * NOTE(danilatos): There is currently no mechanism for jobs to be notified of * cancellation. The reason is that code written to depend on this sort of * functionality can suffer from difficult to debug race conditions. Ideally * code should be structured differently, so that it does not need this * facility. If it is really needed, it can also be implemented on top of this * scheduler fairly simply. We might revisit this decision if it later turns out * to be a much needed feature, in which case there are some easy changes to be * made here to allow it. * * @author danilatos@google.com (Daniel Danilatos) */ public interface Scheduler { /** * This is mainly used to listen to tasks that takes too long to run. * * @author zdwang@google.com (David Wang) */ public interface Listener { /** * The given job is taking too long. */ void onJobExecuted(Schedulable job, int timeSpent); } /** * Tag interface for things the scheduler accepts. */ public interface Schedulable { } /** * A unit of work to be run once. * * NOTE(danilatos): Same as GWT's Command * * The reason for the separate interface is to clarify its coupling to the * Scheduler interface, and the subtly different semantics involving * scheduling an already scheduled task. An additional reason is to provide * the common super interface for type-tagging reasons. */ public interface Task extends Schedulable { /** * Do the entirety of a small amount of work */ void execute(); } /** * A large task to be run incrementally as several units * * NOTE(danilatos): Same as GWT's IncrementalCommand. Often referred to as * "Process" for brevity. * * The reason for the separate interface is the same as for Task */ public interface IncrementalTask extends Schedulable { /** * Do a small unit of a large amount of work * * @return true if there is additional work, false if the task has completed */ boolean execute(); } /** * Priority levels at which tasks execute. These are not distinct merely by * their ordering, but each also has some distinct semantics that are * described below. */ public enum Priority { /** * Jobs that need to not be delayed any more than the single first timeout. * This means that critical tasks do not have the usual time-slice * semantics, and so there is not much point in scheduling an incremental * task at this priority. * * Almost nothing should be critical, because this risks slow script alerts. * Only tasks that depend on having exactly timeout delay for correctness * should be scheduled at this priority, and they should be small. */ CRITICAL, /** * High priority jobs whose correctness doesn't depend on eschewing the * possibility of extra delays. */ HIGH, /** * Between HIGH and LOW. */ MEDIUM, /** * Jobs to run at the lowest priority. Anything that should not hold up high * priority jobs. These jobs may also be additionally throttled when there * is user activity. */ LOW, /** * Do not use. * * NOTE(danilatos): This is a quick hack to allow suppression of individual * tasks for debugging purposes. It is not perfect and when someone has the * time they can do this "properly" without using this enum. * * Individual tasks may be disabled using the schedule knobs, and what this * actually does is cause them to always be scheduled at this super low * priority, which is also disabled by default when the knobs are used. That * is all - enabling this priority will flush the jobs, and it should then * be immediately disabled again to avoid strange behaviour. */ INTERNAL_SUPPRESS; } /** * Tell the scheduler that the user is active. This may cause it to throttle * low priority jobs for a short period. In IE, having excessive background * work can cause undesirable effects such as slow rendering and dropped keys * when typing. It might be that this method is not needed by non-IE * implementations, but should still be called. */ public void noteUserActivity(); /** * Schedule a task to run once at the given priority. * * @param task */ void schedule(Priority priority, Task task); /** * Schedule a process to run continuously at the given priority over a number * over separate time slices until completion. * * @param priority * @param process */ void schedule(Priority priority, IncrementalTask process); /** * Same as {@link #schedule(Priority, Task)}, but begin after a minimumTime * from now * * @param priority * @param task * @param minimumTime * Must be at least zero */ void scheduleDelayed(Priority priority, Task task, int minimumTime); /** * Same as {@link #schedule(Priority, IncrementalTask)}, but begin after a * minimumTime from now * * @param priority * @param process * @param minimumTime * Must be at least zero */ void scheduleDelayed(Priority priority, IncrementalTask process, int minimumTime); /** * Schedule a process to run each unit of work once per given time interval, * starting at least from the given minimum time from now, until completion * * @param priority * @param process * @param minimumTime * Must be at least zero * @param interval * Must be at least zero */ void scheduleRepeating(Priority priority, IncrementalTask process, int minimumTime, int interval); /** * Prevent the given job from running. Calling this on a job that is not * scheduled is a no-op. * * @param job */ void cancel(Schedulable job); /** * @param job * @return true if the given job is already scheduled */ boolean isScheduled(Schedulable job); /** * Adds a listener that may be interested in tasks that takes too long. * @param listener must not be null. */ void addListener(Listener listener); /** * Removes a listener. * @param listener no effect if it's not a known listener. */ void removeListener(Listener listener); /** * @return a short description. */ public String debugShortDescription(); }