package ptolemy.apps.apes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import ptolemy.actor.Actor;
import ptolemy.actor.CompositeActor;
import ptolemy.actor.NoRoomException;
import ptolemy.actor.util.Time;
import ptolemy.apps.apes.TaskExecutionListener.ScheduleEventType;
import ptolemy.data.BooleanToken;
import ptolemy.data.IntToken;
import ptolemy.data.StringToken;
import ptolemy.data.expr.Parameter;
import ptolemy.data.expr.StringParameter;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.NamedObj;
import ptolemy.kernel.util.Workspace;
/**
* The CPU resource implements a fixed priority preemptive scheduling of tasks.
* Actors that can send events to the input of the CPU resource are triggers and
* tasks. Input tokens are ResourceTokens. Output events are sent to tasks,
* output tokens are empty tokens, they are just used to trigger task actors.
* The CPU resource manages a stack of all tasks and their remaining times
* according to the task priorities. When the CPU resource is fired, it
* decreases the remaining time of the currently executing task, puts the tasks
* that are scheduled to be
*
* @author Patricia Derler
*/
public class CPUScheduler extends ApeActor {
/**
* Construct an actor in the default workspace with an empty string as its
* name. The object is added to the workspace directory. Increment the
* version number of the workspace.
*
* @throws IllegalActionException
*/
public CPUScheduler() {
super();
_initialize();
}
/**
* Construct an actor in the specified workspace with an empty string as a
* name. You can then change the name with setName(). If the workspace
* argument is null, then use the default workspace. The object is added to
* the workspace directory. Increment the version number of the workspace.
*
* @param workspace
* The workspace that will list the entity.
*/
public CPUScheduler(Workspace workspace) {
super(workspace);
_initialize();
}
/**
* Create a new actor in the specified container with the specified name.
* The name must be unique within the container or an exception is thrown.
* The container argument must not be null, or a NullPointerException will
* be thrown.
*
* @param container
* The container.
* @param name
* The name of this actor within the container.
* @exception IllegalActionException
* If this actor cannot be contained by the proposed
* container (see the setContainer() method).
* @exception NameDuplicationException
* If the name coincides with an entity already in the
* container.
*/
public CPUScheduler(CompositeEntity container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
_initialize();
}
public enum TaskState {
ready_running, suspended, waiting
}
public enum StatusType {
E_OK, E_OS_ACCESS, E_OS_CALLEVEL, E_OS_ID, E_OS_LIMIT, E_OS_NOFUNC, E_OS_RESOURCE, E_OS_STATE, E_OS_VALUE}
// TODO initialize private variables - maps and lists
public Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
/**
* Schedule actors.
*/
public void fire() throws IllegalActionException {
Time passedTime = getDirector().getModelTime().subtract(_previousModelTime);
Time timeZero = new Time(getDirector(), 0.0);
Actor taskInExecution = null;
Actor newCurrentlyExecutingTask = null;
// if time passed, decrease remaining execution time of currently running task
if (_tasksInExecution.size() > 0 && passedTime.compareTo(timeZero) > 0) {
taskInExecution = _tasksInExecution.peek();
Time remainingTime = _remainingExecutionTime.get(taskInExecution);
if (remainingTime.equals(Time.POSITIVE_INFINITY)) { // task executed but its execution time is not known yet
_usedExecutionTimes.put(taskInExecution, _usedExecutionTimes.get(taskInExecution).add(passedTime));
} else { // task executed, decrease its execution time
remainingTime = remainingTime.subtract(passedTime);
_remainingExecutionTime.put(taskInExecution, remainingTime);
// take out of the list if remainingTime = 0
// task can continue execution, execution time is not known and will be sent by access point event
if (remainingTime.equals(timeZero)) {
_tasksThatStartedExecuting.remove(taskInExecution);
_remainingExecutionTime.put(taskInExecution, Time.POSITIVE_INFINITY);
_usedExecutionTimes.put(taskInExecution, timeZero);
_sendTaskExecutionEvent(taskInExecution, ScheduleEventType.AP);
newCurrentlyExecutingTask = taskInExecution;
}
if (remainingTime.compareTo(timeZero) < 0) {
throw new IllegalActionException("negative remaining execution time "
+ remainingTime + " in task " + taskInExecution +
"\npassed time " + passedTime +
"\ncurrent time " + getDirector().getModelTime()
);
}
}
}
// schedule tasks according to requests sent via tokens
while(input.hasToken(0)) {
ResourceToken token = (ResourceToken) input.get(0);
Actor actorToSchedule = token.actorToSchedule;
Time executionTime = (Time) token.requestedValue;
TaskState state = token.state;
if (state == null)
state = _taskStates.get(actorToSchedule);
Actor actor = scheduleTask(state, actorToSchedule, executionTime);
if (actor != null)
newCurrentlyExecutingTask = actor;
}
if (newCurrentlyExecutingTask != null) {
output.send(newCurrentlyExecutingTask, new BooleanToken(true));
_tasksThatStartedExecuting.add(taskInExecution);
_sendTaskExecutionEvent(newCurrentlyExecutingTask, ScheduleEventType.START);
}
// schedule next firing of this
if (_tasksInExecution.size() > 0) {
Time nextTime = getDirector().getModelTime().add(_remainingExecutionTime.get(_tasksInExecution.peek()));
Time time = getDirector().fireAt(this, nextTime);
}
_previousModelTime = getDirector().getModelTime();
}
/**
* Returns the id of the actor. The id is an int value. The
* default return value is 0.
*
* @param actor
* Given actor.
* @return Priority of the given actor.
*/
public int getTaskId(Actor actor) {
try {
Parameter parameter = (Parameter) ((NamedObj) actor)
.getAttribute("ID");
if (parameter != null) {
IntToken token = (IntToken) parameter.getToken();
return token.intValue();
} else {
return 0;
}
} catch (ClassCastException ex) {
return 0;
} catch (IllegalActionException ex) {
return 0;
}
}
public int getInternalResource(Actor actor) {
try {
Parameter parameter = (Parameter) ((NamedObj) actor)
.getAttribute("internalResourceId");
if (parameter != null) {
IntToken token = (IntToken) parameter.getToken();
return token.intValue();
} else {
return -1;
}
} catch (ClassCastException ex) {
return -1;
} catch (IllegalActionException ex) {
return -1;
}
}
//////////////////////
/// Task management
public int activateTask(int taskId) throws NoRoomException, IllegalActionException {
Actor task = _tasks.get(taskId);
Actor caller = _taskNames.get(Thread.currentThread().getName());
if (task == null)
return 1;
//else if (Task.state = TaskState.running) return StatusType.E_OS_LIMIT;
Actor newCurrentlyExecutingTask = scheduleTask(TaskState.ready_running, task, null);
reschedule(newCurrentlyExecutingTask, (CTask) caller);
return 0;
}
public void terminateTask() throws NoRoomException, IllegalActionException {
Actor task = _taskNames.get(Thread.currentThread().getName());
Actor newCurrentlyExecutingTask = scheduleTask(TaskState.suspended, task, null);
reschedule(newCurrentlyExecutingTask, (CTask) task);
}
public StatusType ChainTask(int taskId) throws NoRoomException, IllegalActionException {
Actor task = _tasks.get(taskId);
Actor currentTask = _taskNames.get(Thread.currentThread().getName());
Actor newCurrentlyExecutingTask1 = scheduleTask(TaskState.suspended, currentTask, null);
Actor newCurrentlyExecutingTask2 = scheduleTask(TaskState.ready_running, task, null);
Actor newCurrentlyExecutingTask = newCurrentlyExecutingTask2 != null ? newCurrentlyExecutingTask2 : newCurrentlyExecutingTask1;
reschedule(newCurrentlyExecutingTask, (CTask) currentTask);
return StatusType.E_OK;
}
@Override
public void wrapup() throws IllegalActionException {
for (Integer taskId : _tasks.keySet()) {
Actor task = _tasks.get(taskId);
_sendTaskExecutionEvent(task, null);
}
}
private Actor scheduleTask(TaskState state, Actor actorToSchedule, Time executionTime) throws IllegalActionException {
Actor newTaskInExecution = null;
if (_taskStates.get(actorToSchedule) == TaskState.waiting && executionTime != null) {
_remainingExecutionTime.put(actorToSchedule, executionTime);
} else if (state == TaskState.waiting) { // remove from queue if waiting
_tasksInExecution.pop();
_sendTaskExecutionEvent(actorToSchedule, ScheduleEventType.WAIT);
newTaskInExecution = getNewTaskInExecution(newTaskInExecution);
} else if (state == TaskState.suspended) {
if (_tasksInExecution.contains(actorToSchedule))
_tasksInExecution.pop();
_tasksThatStartedExecuting.remove(actorToSchedule);
_sendTaskExecutionEvent(actorToSchedule, ScheduleEventType.STOP);
newTaskInExecution = getNewTaskInExecution(newTaskInExecution);
} else {
Actor taskInExecution = null;
if (executionTime != null &&
executionTime.compareTo(new Time(getDirector(), 0.0)) == 0)
return null;
// if task needs an internal resource which is occupied now
// the state should be set to waiting
if (_internalResources.get(actorToSchedule) != null) {
// TODO
}
if (_tasksInExecution.size() > 0)
taskInExecution = _tasksInExecution.peek();
if (taskInExecution == null || _taskPriorities.get(actorToSchedule) > _taskPriorities.get(taskInExecution)) { // nothing running or preempting, schedule new task
if (taskInExecution != null) { // preempting
_sendTaskExecutionEvent(taskInExecution, ScheduleEventType.PREEMPTED);
}
_tasksInExecution.push(actorToSchedule);
_sendTaskExecutionEvent(actorToSchedule, ScheduleEventType.START);
if (!_tasksThatStartedExecuting.contains(actorToSchedule))
newTaskInExecution = actorToSchedule;
} else if (_tasksInExecution.contains(actorToSchedule)) { // already in list but execution time was not known
if (_usedExecutionTimes.get(actorToSchedule) != null &&
executionTime != null)
executionTime = executionTime.subtract(_usedExecutionTimes.get(actorToSchedule));
} else { // new actor to schedule does not preempt currently running task
for (int i = 0; i < _tasksInExecution.size(); i++) {
Actor actor = _tasksInExecution.get(i);
if (_taskPriorities.get(actorToSchedule) < _taskPriorities.get(actor)) {
_tasksInExecution.insertElementAt(actorToSchedule, i);
break;
}
}
}
if (executionTime != null)
_remainingExecutionTime.put(actorToSchedule, executionTime);
_usedExecutionTimes.put(actorToSchedule, new Time(getDirector(), 0.0));
}
_taskStates.put(actorToSchedule, state);
return newTaskInExecution;
}
private Actor getNewTaskInExecution(Actor newTaskInExecution) {
if (_tasksInExecution.size() > 0) {
Actor actor = _tasksInExecution.peek();
_sendTaskExecutionEvent(actor, ScheduleEventType.START);
if (!_tasksThatStartedExecuting.contains(_tasksInExecution.peek()))
newTaskInExecution = _tasksInExecution.peek();
}
return newTaskInExecution;
}
private void reschedule(Actor newTaskInExecution, CTask callingTask) throws IllegalActionException {
if (newTaskInExecution != null) {
// if (callingTask != null) TODO check
// callingTask.bufferOutput(newTaskInExecution, new BooleanToken(true));
// else
output.send(newTaskInExecution, new BooleanToken(true));
_tasksThatStartedExecuting.add(newTaskInExecution);
_sendTaskExecutionEvent(newTaskInExecution, ScheduleEventType.START);
}
// schedule next firing of this
if (_tasksInExecution.size() > 0) {
Time nextTime = getDirector().getModelTime().add(_remainingExecutionTime.get(_tasksInExecution.peek()));
Time time = getDirector().fireAt(this, nextTime);
}
}
private HashMap<Actor, List<Integer>> _occupiedResources = new HashMap();
private HashMap<Actor, Integer> _internalResources = new HashMap();
private List<Integer> _resources = new ArrayList();
/**
* release resources if higher priority task is ready
* * @throws IllegalActionException
* @throws NoRoomException
*/
public StatusType Schedule() throws NoRoomException, IllegalActionException {
Actor task = _taskNames.get(Thread.currentThread().getName());
if (_internalResources.get(task) != null) {
for (Actor actor : _taskStates.keySet()) {
if (_tasksInExecution.size() > 0 &&
_taskPriorities.get(actor) > _taskPriorities.get(_tasksInExecution.peek()) &&
_internalResources.get(actor) == _internalResources.get(_tasksInExecution.peek())) {
Actor newCurrentlyExecutingTask = scheduleTask(TaskState.suspended, task, null);
reschedule(newCurrentlyExecutingTask, (CTask) task);
}
}
}
getDirector().fireAt(this, getDirector().getModelTime());
// if (task with higher priority is scheduled and needs this resource)
// release resource
return StatusType.E_OK;
}
//////////////////////
/// Resource management
// DeclareResource
public StatusType GetResource(int resourceId) {
Actor task = _taskNames.get(Thread.currentThread().getName());
for (Actor actor : _occupiedResources.keySet()) {
List resources = _occupiedResources.get(actor);
if (resources.contains(resourceId))
return StatusType.E_OS_ACCESS; // resource in use
}
if (!_resources.contains(resourceId))
return StatusType.E_OS_ID;
else {
List l = _occupiedResources.get(task);
if (l == null)
l = new ArrayList();
l.add(resourceId);
_occupiedResources.put(task, l);
}
return StatusType.E_OK;
}
public StatusType ReleaseResource(int resourceId) {
Actor task = _taskNames.get(Thread.currentThread().getName());
if (!_resources.contains(resourceId))
return StatusType.E_OS_ID;
else if (_occupiedResources.get(task) == null ||
!_occupiedResources.get(task).contains(resourceId)) // not occupied
return StatusType.E_OS_NOFUNC;
_occupiedResources.get(task).remove(resourceId);
return StatusType.E_OK;
}
public static OSEKEntryPoint osekEntryPoint;
public EventManager eventManager;
/**
* Set private variables.
*/
public void preinitialize() throws IllegalActionException {
super.preinitialize();
try {
_previousModelTime = new Time(getDirector(), 0.0);
} catch (IllegalActionException e) {
e.printStackTrace();
}
CompositeActor compositeActor = (CompositeActor) getContainer();
List entities = compositeActor.entityList();
for (Iterator it = entities.iterator(); it.hasNext();) {
Object entity = it.next();
if (entity instanceof Actor) {
Actor actor = (Actor) entity;
if (actor instanceof CTask) {
_tasks.put(getTaskId(actor), actor);
_taskStates.put(actor, TaskState.suspended);
_remainingExecutionTime.put(actor, Time.POSITIVE_INFINITY);
_usedExecutionTimes.put(actor, getDirector().getModelTime()); // during init, this is 0
_taskActivations.put(actor, 0);
_taskNames.put(actor.getName(), actor);
_taskPriorities.put(actor, CTask.getPriority(actor));
if (getInternalResource(actor) != -1)
_internalResources.put(actor, getInternalResource(actor));
} else if (actor instanceof EventManager) {
eventManager = (EventManager) actor;
}
}
}
}
@Override
public void initialize() throws IllegalActionException {
super.initialize();
if (osekEntryPoint == null) {
osekEntryPoint = new OSEKEntryPoint(this, eventManager);
try {
String libName = ((StringToken)((StringParameter)((NamedObj)getContainer()).getAttribute("CCodeLibrary")).getToken()).stringValue();
osekEntryPoint.InitializeC();
} catch (Exception ex) {
}
}
}
/**
* Register task execution listener.
* @param taskExecutionListener
*/
protected void _registerExecutionListener(
TaskExecutionListener taskExecutionListener) {
if (_executionListeners == null)
_executionListeners = new ArrayList<TaskExecutionListener>();
_executionListeners.add(taskExecutionListener);
}
/**
* Send event to the task execution listeners.
* @param actor
* @param time
* @param eventType
*/
protected final void _sendTaskExecutionEvent(Actor actor,
ScheduleEventType eventType) {
if (_executionListeners != null) {
Iterator listeners = _executionListeners.iterator();
while (listeners.hasNext()) {
((TaskExecutionListener) listeners.next()).event(actor, getDirector().getModelTime().getDoubleValue(),
eventType);
}
}
}
public native void InitializeC();
/**
* Initialize private variables.
*/
private void _initialize() {
_remainingExecutionTime = new HashMap<Actor, Time>();
_usedExecutionTimes = new HashMap<Actor, Time>();
_tasksInExecution = new Stack<Actor>();
_taskStates = new HashMap<Actor, TaskState>();
_tasks = new HashMap();
_taskActivations = new HashMap();
_taskNames = new HashMap();
_taskPriorities = new HashMap();
Parameter sourceActorList= (Parameter) input.getAttribute("sourceActors");
sourceActorList.setExpression("*");
Parameter destinationActorList= (Parameter) output.getAttribute("destinationActors");
destinationActorList.setExpression("*");
}
/** Set of tasks that were started and already executed for some time. Those tasks can be preempted. */
private Set _tasksThatStartedExecuting = new HashSet();
/** Map of taskIds to tasks. */
private Map<Integer, Actor> _tasks;
/** Map of taskNames and tasks. */
private Map<String, Actor> _taskNames;
/** Map of actual Task priorities. */
private Map<Actor, Integer> _taskPriorities;
/** The number of pending activation requests for a task. */
private HashMap<Actor, Integer> _taskActivations;
/** Tasks in execution and their remaining execution time. */
private Map<Actor, Time> _remainingExecutionTime;
/** Tasks in execution and their used execution times. This is used when the real execution
* time is not known yet and the remaining execution time (= infinity) cannot be decreased
* yet.
*/
private Map<Actor, Time> _usedExecutionTimes;
/** Tasks in execution. */
private Stack<Actor> _tasksInExecution;
/** Model time at the previous firing. */
private Time _previousModelTime;
/** List of all tasks and their current state */
private Map<Actor, TaskState> _taskStates;
/** Listeners for the task execution events: start, preempt, resume. */
private Collection<TaskExecutionListener> _executionListeners;
}