using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; using System.Threading; namespace Eraser.Manager { /// /// The DirectExecutor class is used by the Eraser GUI directly when the program /// is run without the help of a Service. /// public class DirectExecutor : Executor, IDisposable { public DirectExecutor() { thread = new Thread(delegate() { this.Main(); }); thread.Start(); Thread.Sleep(0); } void IDisposable.Dispose() { thread.Abort(); schedulerInterrupt.Set(); } public override void AddTask(ref Task task) { lock (unusedIdsLock) { if (unusedIds.Count != 0) { task.ID = unusedIds[0]; unusedIds.RemoveAt(0); } else task.ID = ++nextId; } //Add the task to the set of tasks lock (tasksLock) { tasks.Add(task.ID, task); //If the task is scheduled to run now, break the waiting thread and //run it immediately if (task.Schedule == Schedule.RunNow) { scheduledTasks.Add(DateTime.Now, task); schedulerInterrupt.Set(); } //If the task is scheduled, add the next execution time to the list //of schduled tasks. else if (task.Schedule != Schedule.RunOnRestart) { scheduledTasks.Add((task.Schedule as RecurringSchedule).NextRun, task); } } } public override bool DeleteTask(uint taskId) { lock (tasksLock) { if (!tasks.ContainsKey(taskId)) return false; lock (unusedIdsLock) unusedIds.Add(taskId); tasks.Remove(taskId); } return true; } public override Task GetTask(uint taskId) { lock (tasksLock) { if (!tasks.ContainsKey(taskId)) return null; return tasks[taskId]; } } public override Dictionary.Enumerator GetIterator() { return tasks.GetEnumerator(); } /// /// The thread entry point for this object. This object operates on a queue /// and hence the thread will sequentially execute tasks. /// private void Main() { //The waiting thread will utilize a polling loop to check for new //scheduled tasks. This will be checked every 30 seconds. However, //when the thread is waiting for a new task, it can be interrupted. while (thread.ThreadState != ThreadState.AbortRequested) { //Check for a new task Task task = null; lock (tasksLock) { if (scheduledTasks.Count != 0 && (scheduledTasks.Values[0].Schedule == Schedule.RunNow || scheduledTasks.Keys[0] <= DateTime.Now)) { task = scheduledTasks.Values[0]; scheduledTasks.RemoveAt(0); } } if (task != null) { //Run the task ; //If the task is a recurring task, reschedule it since we are done. if (task.Schedule is RecurringSchedule) ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now); } //Wait for half a minute to check for the next scheduled task. schedulerInterrupt.WaitOne(30000, false); } } /// /// The thread object. /// private Thread thread; /// /// The lock preventing concurrent access for the tasks list and the /// tasks queue. /// private object tasksLock = new object(); /// /// The list of tasks. Includes all immediate, reboot, and recurring tasks /// private Dictionary tasks = new Dictionary(); /// /// The queue of tasks. This queue is executed when the first element's /// timestamp (the key) has been past. This list assumes that all tasks /// are sorted by timestamp, smallest one first. /// private SortedList scheduledTasks = new SortedList(); /// /// The list of task IDs for recycling. /// private List unusedIds = new List(); /// /// Lock preventing concurrent access for the IDs. /// private object unusedIdsLock = new object(); /// /// Incrementing ID. This value is incremented by one every time an ID /// is required by no unused IDs remain. /// private uint nextId = 0; /// /// An automatically reset event allowing the addition of new tasks to /// interrupt the thread's sleeping state waiting for the next recurring /// task to be due. /// AutoResetEvent schedulerInterrupt = new AutoResetEvent(true); } }