source: branches/eraser6/Manager/DirectExecutor.cs @ 171

Revision 171, 4.6 KB checked in by lowjoel, 6 years ago (diff)

Allow the DirectExecutor? class to be disposable, thus ending the thread when the program is exiting thus allowing immediate exit.

Line 
1using System;
2using System.Collections.Generic;
3using System.Collections.Specialized;
4using System.Text;
5using System.Threading;
6
7namespace Eraser.Manager
8{
9    /// <summary>
10    /// The DirectExecutor class is used by the Eraser GUI directly when the program
11    /// is run without the help of a Service.
12    /// </summary>
13    public class DirectExecutor : Executor, IDisposable
14    {
15        public DirectExecutor()
16        {
17            thread = new Thread(delegate()
18            {
19                this.Main();
20            });
21
22            thread.Start();
23            Thread.Sleep(0);
24        }
25
26        void IDisposable.Dispose()
27        {
28            thread.Abort();
29            schedulerInterrupt.Set();
30        }
31
32        public override void AddTask(ref Task task)
33        {
34            lock (unusedIdsLock)
35            {
36                if (unusedIds.Count != 0)
37                {
38                    task.ID = unusedIds[0];
39                    unusedIds.RemoveAt(0);
40                }
41                else
42                    task.ID = ++nextId;
43            }
44
45            //Add the task to the set of tasks
46            lock (tasksLock)
47            {
48                tasks.Add(task.ID, task);
49
50                //If the task is scheduled to run now, break the waiting thread and
51                //run it immediately
52                if (task.Schedule == Schedule.RunNow)
53                {
54                    scheduledTasks.Add(DateTime.Now, task);
55                    schedulerInterrupt.Set();
56                }
57                //If the task is scheduled, add the next execution time to the list
58                //of schduled tasks.
59                else if (task.Schedule != Schedule.RunOnRestart)
60                {
61                    scheduledTasks.Add((task.Schedule as RecurringSchedule).NextRun, task);
62                }
63            }
64        }
65
66        public override bool DeleteTask(uint taskId)
67        {
68            lock (tasksLock)
69            {
70                if (!tasks.ContainsKey(taskId))
71                    return false;
72
73                lock (unusedIdsLock)
74                    unusedIds.Add(taskId);
75                tasks.Remove(taskId);
76            }
77            return true;
78        }
79
80        public override Task GetTask(uint taskId)
81        {
82            lock (tasksLock)
83            {
84                if (!tasks.ContainsKey(taskId))
85                    return null;
86                return tasks[taskId];
87            }
88        }
89
90        public override Dictionary<uint, Task>.Enumerator GetIterator()
91        {
92            return tasks.GetEnumerator();
93        }
94
95        /// <summary>
96        /// The thread entry point for this object. This object operates on a queue
97        /// and hence the thread will sequentially execute tasks.
98        /// </summary>
99        private void Main()
100        {
101            //The waiting thread will utilize a polling loop to check for new
102            //scheduled tasks. This will be checked every 30 seconds. However,
103            //when the thread is waiting for a new task, it can be interrupted.
104            while (thread.ThreadState != ThreadState.AbortRequested)
105            {
106                //Check for a new task
107                Task task = null;
108                lock (tasksLock)
109                {
110                    if (scheduledTasks.Count != 0 &&
111                        (scheduledTasks.Values[0].Schedule == Schedule.RunNow ||
112                         scheduledTasks.Keys[0] <= DateTime.Now))
113                    {
114                        task = scheduledTasks.Values[0];
115                        scheduledTasks.RemoveAt(0);
116                    }
117                }
118
119                if (task != null)
120                {
121                    //Run the task
122                    ;
123
124                    //If the task is a recurring task, reschedule it since we are done.
125                    if (task.Schedule is RecurringSchedule)
126                        ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now);
127                }
128
129                //Wait for half a minute to check for the next scheduled task.
130                schedulerInterrupt.WaitOne(30000, false);
131            }
132        }
133
134        /// <summary>
135        /// The thread object.
136        /// </summary>
137        private Thread thread;
138
139        /// <summary>
140        /// The lock preventing concurrent access for the tasks list and the
141        /// tasks queue.
142        /// </summary>
143        private object tasksLock = new object();
144
145        /// <summary>
146        /// The list of tasks. Includes all immediate, reboot, and recurring tasks
147        /// </summary>
148        private Dictionary<uint, Task> tasks = new Dictionary<uint, Task>();
149
150        /// <summary>
151        /// The queue of tasks. This queue is executed when the first element's
152        /// timestamp (the key) has been past. This list assumes that all tasks
153        /// are sorted by timestamp, smallest one first.
154        /// </summary>
155        private SortedList<DateTime, Task> scheduledTasks =
156            new SortedList<DateTime, Task>();
157
158        /// <summary>
159        /// The list of task IDs for recycling.
160        /// </summary>
161        private List<uint> unusedIds = new List<uint>();
162
163        /// <summary>
164        /// Lock preventing concurrent access for the IDs.
165        /// </summary>
166        private object unusedIdsLock = new object();
167
168        /// <summary>
169        /// Incrementing ID. This value is incremented by one every time an ID
170        /// is required by no unused IDs remain.
171        /// </summary>
172        private uint nextId = 0;
173
174        /// <summary>
175        /// An automatically reset event allowing the addition of new tasks to
176        /// interrupt the thread's sleeping state waiting for the next recurring
177        /// task to be due.
178        /// </summary>
179        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
180    }
181}
Note: See TracBrowser for help on using the repository browser.