/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package org.absmodels.abs.plugin.debug.scheduling; import static org.absmodels.abs.plugin.debug.DebugUtils.*; import static org.absmodels.abs.plugin.util.UtilityFunctions.showErrorMessage; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.absmodels.abs.plugin.debug.DebugUtils; import org.absmodels.abs.plugin.debug.perspective.SchedulerChoiceDelegate; import org.absmodels.abs.plugin.debug.views.debugview.DebugView; import org.eclipse.jface.action.IMenuCreator; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.widgets.Display; import abs.backend.java.debugging.TaskState; import abs.backend.java.observing.TaskStackFrameView; import abs.backend.java.observing.TaskStackView; import abs.backend.java.observing.TaskView; import abs.backend.java.scheduling.ActivateTask; import abs.backend.java.scheduling.ScheduleAction; import abs.backend.java.scheduling.ScheduleOptions; import abs.backend.java.scheduling.UsesRandomSeed; import abs.backend.java.scheduling.SimpleTaskScheduler.TaskInfo; import abs.backend.java.scheduling.TaskScheduler; import abs.backend.java.scheduling.TotalSchedulingStrategy; public class SchedulingStrategy implements TotalSchedulingStrategy, UsesRandomSeed { private boolean doDebug = true; private List<ScheduleAction> scheduleActions; private History history; boolean waitForUserInput = true; List<TaskView> schedulableTasks; TotalScheduler curScheduler; TotalScheduler baseScheduler; abs.backend.java.debugging.TaskInfo steppedTask = null; private Random random = null; public SchedulingStrategy() { schedulerRef = this; history = new History(); curScheduler = new GUIScheduler(this); baseScheduler = new RunTaskScheduler(this); } /** * Sets the current scheduler. If the old scheduler is the GUIScheduler, it is interrupted * and notified about the changed scheduler. * @param scheduler */ public synchronized void setCurrentScheduler(TotalScheduler scheduler){ if (random != null && scheduler instanceof UsesRandomSeed) { ((UsesRandomSeed) scheduler).setRandom(random); } TotalSchedulingStrategy oldscheduler = curScheduler; curScheduler = scheduler; schedulerUpdated(oldscheduler); } /** * Sets the base scheduler. If the new base scheduler is the TaskScheduler the selected task * is set on the scheduler * @param scheduler */ public synchronized void setBaseScheduler(TotalScheduler scheduler){ if (random != null && scheduler instanceof UsesRandomSeed) { ((UsesRandomSeed) scheduler).setRandom(random); } this.baseScheduler = scheduler; if(baseScheduler instanceof RunTaskScheduler){ RunTaskScheduler ts = (RunTaskScheduler) baseScheduler; Object o = getDebugViewerSelection(); if(o instanceof TaskView) ts.setTask((TaskView) o); } } /** * resumes the scheduling with the current base scheduler (Interrupts the * GUIScheduler if it is the current scheduler) */ public synchronized void resumeAutomaticScheduling(){ TotalSchedulingStrategy oldScheduler = curScheduler; baseScheduler.reset(); curScheduler = baseScheduler; disableHighlighting(); schedulerUpdated(oldScheduler); } private void schedulerUpdated(TotalSchedulingStrategy oldScheduler) { if(oldScheduler instanceof GUIScheduler){ GUIScheduler gs = (GUIScheduler) oldScheduler; gs.interrupt(); notifyAll(); } } /** * Gets called if the selection of the {@link DebugView} {@link TreeViewer} changes * @param o the selection */ public void taskSelectionChanged(Object o){ if(baseScheduler instanceof RunTaskScheduler){ RunTaskScheduler ts = (RunTaskScheduler) baseScheduler; if(o instanceof TaskView){ ts.setTask((TaskView) o); } else if(o instanceof TaskStackFrameView){ ts.setTask(((TaskStackFrameView)o).getStack().getTask()); } } } /** * Has to be called when the selection of the current scheduler in the scheduler menu * has changed. Updates the current base scheduler accordingly. */ public void updateScheduler(){ if (DebugUtils.schedulerMenu != null && schedulerMenu.getMenuCreator() instanceof SchedulerChoiceDelegate) { // update selection in UI SchedulerChoiceDelegate menu = (SchedulerChoiceDelegate) schedulerMenu.getMenuCreator(); menu.setSelection(DebugUtils.getScheduler()); } switch(DebugUtils.getScheduler()){ case interactive: setBaseScheduler(new RunTaskScheduler(this)); refreshButtonEnablement(); break; case random: setBaseScheduler(new RandomScheduler(new Random())); refreshButtonEnablement(); break; case replay: setBaseScheduler(new HistoryReplayScheduler(this)); refreshButtonEnablement(); break; default: throw new RuntimeException("Non exhaustive match in scheduler enum" + " while updating scheduler in SchedulingStrategy!"); } } /** * Has to be called when the RunTime shuts down */ public synchronized void systemFinished(){ scheduleActions = null; schedulableTasks = null; } public synchronized List<TaskView> getSchedulableTasks(){ return schedulableTasks; } @Override public ScheduleAction choose(ScheduleOptions options) { synchronized (this) { scheduleActions = options.allOptions(); schedulableTasks = new ArrayList<TaskView>(); for (ScheduleAction a : scheduleActions) { if(a.getTask() != null){ schedulableTasks.add(a.getTask()); } else { schedulableTasks.addAll(a.getCOG().getView().getScheduler().getSchedulableTasks()); } } } if(highlightStep){ highlightNextStep(); } ScheduleAction a = curScheduler.choose(options); // is null when the system was terminated if (a != null) { logAction(a); history.chosenAction(a); } return a; } /** * Updates the graphical display after a step. Refreshes the buttons enablement and * selects the next stepable task in the {@link DebugView} */ public void highlightNextStep() { Display.getDefault().asyncExec(new Runnable() { @Override public void run() { refreshButtonEnablement(); if(baseScheduler instanceof RunTaskScheduler && steppedTaskIsSchedulable()){ selectNextTask(); } else if(baseScheduler instanceof RandomScheduler){ if (steppedTask != null) { setDebugTreeSelection(steppedTask.getTaskView()); } } refreshVariableView(); refreshDebugViewer(); } }); } private boolean steppedTaskIsSchedulable() { return steppedTask != null && ((steppedTask.getState() == TaskState.FINISHED) || (steppedTask.getState() == TaskState.SUSPENDED && !schedulableTasks.contains(steppedTask.getTaskView()))); } public void setLastTask(abs.backend.java.debugging.TaskInfo lastTask){ steppedTask = lastTask; } /** * Gets called when the user selects to run to a specific line (in a file) * @param file * @param line */ public synchronized void doRunToLine(String file, int line){ disableHighlighting(); setCurrentScheduler(new RunToLineScheduler(this, file, line)); } /** * Gets called when the user selects to do multiple steps * @param n the number of steps to perform */ public synchronized void doMultipleSteps(int n){ disableHighlighting(); baseScheduler.reset(); setCurrentScheduler(new NStepScheduler(this, n)); } /** * Gets called when the user selects to do a single step */ public synchronized void doSingleStep() { waitForUserInput = false; notify(); } /** * Gets called when the user selects to do a step over */ public synchronized void doStepOver(){ if(steppedTask!=null){ TaskStackView stack = steppedTask.getTaskView().getStack(); if(stack.hasFrames()){ disableHighlighting(); setCurrentScheduler(new StepOverScheduler(this, stack.getCurrentFrame())); } else { doSingleStep(); } } else { doSingleStep(); } } public synchronized List<ScheduleAction> getScheduleActions(){ return scheduleActions; } @Override public TaskInfo schedule(final TaskScheduler scheduler, List<TaskInfo> scheduableTasks) { TaskInfo task = curScheduler.schedule(scheduler, scheduableTasks); logTaskInfo(task); history.activatedTask(task); return task; } private void logAction(ScheduleAction a) { if(doDebug) System.out.println("chosen action: "+a); } private void logTaskInfo(TaskInfo t) { if(doDebug) System.out.println("chosen task: "+t.toString()); } public void saveHistory(File file){ history.saveHistory(file); } class History{ List<ScheduleAction> history; public History(){ history = new ArrayList<ScheduleAction>(); } private void saveHistory(File f){ try { PrintWriter p = new PrintWriter(new FileOutputStream(f)); for (ScheduleAction a : history) { p.append(a.shortString()); p.append("\n"); } p.close(); } catch (FileNotFoundException e) { showErrorMessage("Could not save history: " + e.getMessage()); } } public void activatedTask(TaskInfo task){ history.add(new ActivateTask(task.task.getCOG(), task.task)); } public void chosenAction(ScheduleAction a){ history.add(a); } } /** * Waits for user user decision on which action the scheduler should take next. * @param options the current scheduling options coming from the debugger in the abs frontend * @return a valid scheduling option */ ScheduleAction awaitGUIAction(ScheduleOptions options) { setCurrentScheduler(new GUIScheduler(this)); enableHightlighting(); return curScheduler.choose(options); } @Override public void setRandom(Random random) { this.random = random; } }