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);
}
}