![JAR search and dependency download from the Maven repository](/logo.png)
gnu.cajo.utils.extra.Scheduler Maven / Gradle / Ivy
Show all versions of ghost4j Show documentation
package gnu.cajo.utils.extra;
import java.io.*;
import gnu.cajo.invoke.Remote;
/*
* General purpose cooperative task scheduler
* Copyright (c) 1999 John Catherino
* The cajo project: https://cajo.dev.java.net
*
* For issues or suggestions mailto:[email protected]
*
* This file Registry.java is part of the cajo library.
*
* The cajo library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public Licence as published
* by the Free Software Foundation, at version 3 of the licence, or (at your
* option) any later version.
*
* Th cajo library 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 Licence for more details.
*
* You should have received a copy of the GNU Lesser General Public Licence
* along with this library. If not, see http://www.gnu.org/licenses/lgpl.html
*/
/**
* This class is for scheduling the non-preemptive execution of tasks. The
* class creates a single thread, and will independently manange and run its
* loaded objects. The Scheduler accepts objects for scheduling, at any time
* during its existence. The scheduled object must implement no-arg, void
* slice() method, to be used as a time slice, in which to perform some fixed
* piece of functionality. The scheduler will provide the calling mechanism.
* An objectwith this type of method implementation will be considered a task
* for the purposes of this class.
*
* The scheduler's purpose is to provide an exclusive thread of execution
* amongst the scheduled tasks. It will assure that only one of the tasks is
* running at any given time, therefore shared memory between the tasks
* cannot be corrupted by the scheduler due to synchronization problems.
*
* All scheduler methods are properly synchronized, to allow task loading,
* unloading, and management, from other threads, as well as by the scheduled
* tasks themselves.
*
* Up to 32 tasks can be loaded, at any given time, for scheduling. Once
* loaded, tasks can be scheduled to run in any, or all, of three ways;
* Synchronous, Triggered, or Asynchronous. The class implements three
* methods to flag a loaded task as such. The following is a description of
* the scheduling algorithm:
*
* When a task is flagged as asynchronous, it will be run only when no
* tasks are flagged as synchronous, or triggered, at the start of its
* slice. Its asynchronous flag is not cleared when it is run, meaning until
* stopped, it will be run again automatically, as scheduling permits.
* Scheduling among multiple asynchronous flags is round-robbin, to ensure
* each an opportunity to run. This provides a method for scheduling
* low-priority tasks. Typically an asynchronous task will break its
* functionality into distinct pieces, and use a switch() type mechanism to
* execute only one piece per timeslice.
*
* When a task is flagged as triggered, will run before any tasks flagged
* asynchronous, but only when no tasks are flagged as synchronous, at the
* start of its slice. The trigger flag is cleared when the task is run,
* meaning that unless re-flagged as triggered, it will not run again
* through the trigger scheduling algorithm. This provides an event-driven
* mode of execution, but at a lower priority than the synchronous mode.
*
* When a task is flagged as synchronous, it will run before any triggered,
* or asynchronous flagged tasks are allowed to run. Its synchronous flag
* is also cleared when the task is run. Since the scheduling is not
* preemptive, any currently running task is allowed to complete. This flag
* provides the highest responsiveness to a synchronizing event.
*
* The stop method is used to prevent the execution of any flagged task.
* General notes:
* -
* Since scheduling is not preemptive, developers must agressively minimize
* asynchronous task's run time length, to decrease the latency of event
* responsiveness. This becomes especially important when tasks are elevated
* in priority.
*
-
* Non-preemptive scheduling eliminates all synchronization concerns for
* objects shared between scheduled tasks.
*
-
* As a general rule: try to use asynchronous scheduling initially. Then, as
* the design runtime load increases, selected tasks can be boosted in
* proiority to achieve the desired responsiveness.
*
* Note: This class supports serialisation. It will restart the
* scheduling task automatically on deserialisation. However, in order for
* serialisation to succeed, all of the loaded tasks must also be
* serialisable.
*
* @version 1.0, 01-Nov-99 Initial release
* @author John Catherino
*
*/
public final class Scheduler implements Serializable {
private static final String
INDEX_INVALID = "task table index invalid";
private transient Thread thread;
private int syncFlags, soonFlags, wakeFlags;
private Object list[] = new Object[32];
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
setEnabled(true);
}
private final Runnable kernel = new Runnable() {
public void run() {
try {
int next = 0;
while (!thread.isInterrupted()) {
int slot = 0;
synchronized(Scheduler.this) {
if (syncFlags != 0) {
int mask = 1;
while((syncFlags & mask) == 0) {
slot++;
mask <<= 1;
}
syncFlags ^= mask;
} else if (soonFlags != 0) {
int mask = 1;
while((soonFlags & mask) == 0) {
slot++;
mask <<= 1;
}
soonFlags ^= mask;
} else if (wakeFlags != 0) {
for (int i = 0; i < 32; i++) {
if (++next == 32) next = 0;
if ((1 << next & wakeFlags) != 0) {
slot = next;
break;
}
}
} else {
Scheduler.this.wait();
continue;
}
}
try { Remote.invoke(list[slot], "slice", null); }
catch(Exception x) { drop(slot); }
}
} catch(InterruptedException x) {}
}
};
/**
* Nothing is performed in the constructor, since no tasks can be
* scheduled for execution until they have been loaded.
*/
public Scheduler() {}
/**
* This method will start, or suspend, the scheduler. The scheduler will
* be started automatically when the first task is flagged for execution.
* The method is idempotent; therefore enabling an already enabled
* scheduler will cause no effect, just as disabling a currently disabled
* scheduler.
* @param enabled The flag to indicate if this is a startup, or suspend
* operation.
* @return true if successfully started or stopped, false if not for
* logical reasons, i.e. already stopped/started, no tasks running.
*/
public synchronized boolean setEnabled(boolean enabled) {
if (enabled) {
if (syncFlags != 0 | soonFlags != 0 | wakeFlags != 0) {
if (thread == null) {
thread = new Thread(kernel);
thread.start();
return true;
} else notify();
}
} else {
if (thread != null) {
thread.interrupt();
thread = null;
return true;
}
}
return false;
}
/**
* This method accepts a task to be scheduled. If the task is accepted,
* it is placed in the table, but will not be executed, until it is
* flagged for operation. If the execution of the task slice results in
* an exception, the task will be automatically dropped from the queue.
* @param task The task to attempt to schedule, it may be either local,
* remote, or even a proxy, when enabled.
* @return the index of the task in the table. The task table index is
* used in the scheduling methods.
* @throws IllegalArgumentException If the task table is full.
*/
public synchronized int load(Object task) {
for (int slot = 0; slot < 32; slot++) {
if (list[slot] == null) {
list[slot] = task;
return slot;
}
}
throw new IllegalArgumentException("task table currently full");
}
/**
* This method sets the synchronous execution flag for a task. The flag
* will be cleared automatically, when the scheduler calls this task.
* @param task The index of the task in the table
* @return false if the task was already flagged, or not in the queue,
* else true if successfully flagged.
* @throws IllegalArgumentException If the task table index is invalid.
*/
public synchronized boolean sync(int task) {
if (task >= 0 && task < 32) {
int mask = 1 << task;
if ((syncFlags & mask) == 0) {
syncFlags |= mask;
setEnabled(true);
return true;
} else return false;
} else throw new IllegalArgumentException(INDEX_INVALID);
}
/**
* This method sets the triggered execution flag for a task. It will be
* cleared just before passing execution on to the task.
* @param task The index of the task in the table
* @return true if successfully flagged, false if already flagged
* @throws IllegalArgumentException If the task table index is invalid.
*/
public synchronized boolean soon(int task) {
if (task >= 0 && task < 32) {
int mask = 1 << task;
if ((soonFlags & mask) == 0) {
soonFlags |= mask;
setEnabled(true);
return true;
} else return false;
} else throw new IllegalArgumentException(INDEX_INVALID);
}
/**
* This method sets the asynchronous execution flag for a task. It will
* not be cleared when passing execution on to the task. The flag
* will retain its state, until disabled through the stop method.
* @param task The index of the task to be scheduled for continuous
* asynchronous execution.
* @return true if successfully flagged, false if already flagged.
* @throws IllegalArgumentException If the task table index is invalid.
*/
public synchronized boolean wake(int task) {
if (task >= 0 && task < 32) {
int mask = 1 << task;
if ((wakeFlags & mask) == 0) {
wakeFlags |= mask;
setEnabled(true);
return true;
} else return false;
} else throw new IllegalArgumentException(INDEX_INVALID);
}
/**
* This method clears all scheduling flags for indicated task. The task
* will reamain in the table however. Note: to remove a task, use
* the drop method instead, it automatically calls stop, before removing
* the task from the table.
* @param task The index of the task in the table
* @throws IllegalArgumentException If the task table index is invalid.
*/
public synchronized void stop(int task) {
if (task >= 0 && task < 32) {
int mask = ~(1 << task);
syncFlags &= mask;
soonFlags &= mask;
wakeFlags &= mask;
} else throw new IllegalArgumentException(INDEX_INVALID);
}
/**
* This method clears all scheduling flags for the indicated task, and
* also removes it from the table.
* @param task The index of the task to stop and remove from the table.
* @throws IllegalArgumentException If the task table index is invalid.
*/
public synchronized void drop(int task) {
if (task >= 0 && task < 32) {
stop(task);
list[task] = null;
} else throw new IllegalArgumentException(INDEX_INVALID);
}
/**
* The purpose of this function is to reduce event-driven task latency by
* allowing asynchronous tasks to voluntarily exit prematurely. To
* improve responsiveness, asynchronous tasks could check this method,
* before going on to another functionally distinct section of its task
* execution. It is normally checked when the async task has some periodic
* extra work, if the method returns false, the extra work could be
* processed in its current slice.
* @return true if synchronous or triggered tasks have been flagged for
* execution, false if it is OK for the task to continue running a little
* longer.
*/
public boolean pending() {
return syncFlags != 0 | soonFlags != 0;
}
}