source: branches/eraser6/pluginsRewrite/Eraser.Manager/DirectExecutor.cs @ 2366

Revision 2366, 13.3 KB checked in by lowjoel, 2 years ago (diff)

Since the types have been moved to the Eraser.Plugins.ExtensionPoints? namespace, reference that.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By: Kasra Nassiri <cjax@users.sourceforge.net> @17/10/2008
6 * Modified By:
7 *
8 * This file is part of Eraser.
9 *
10 * Eraser is free software: you can redistribute it and/or modify it under the
11 * terms of the GNU General Public License as published by the Free Software
12 * Foundation, either version 3 of the License, or (at your option) any later
13 * version.
14 *
15 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
16 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 *
19 * A copy of the GNU General Public License can be found at
20 * <http://www.gnu.org/licenses/>.
21 */
22
23using System;
24using System.Collections.Generic;
25using System.Collections.Specialized;
26using System.Text;
27using System.Threading;
28using System.IO;
29using System.Runtime.Serialization;
30using System.Runtime.Serialization.Formatters.Binary;
31
32using Eraser.Util;
33using Eraser.Util.ExtensionMethods;
34using Eraser.Plugins.ExtensionPoints;
35
36namespace Eraser.Manager
37{
38    /// <summary>
39    /// The DirectExecutor class is used by the Eraser GUI directly when the program
40    /// is run without the help of a Service.
41    /// </summary>
42    public class DirectExecutor : Executor
43    {
44        public DirectExecutor()
45        {
46            TaskAdded += OnTaskAdded;
47            TaskDeleted += OnTaskDeleted;
48            tasks = new DirectExecutorTasksCollection(this);
49            thread = new Thread(Main);
50        }
51
52        protected override void Dispose(bool disposing)
53        {
54            if (thread == null || schedulerInterrupt == null)
55                return;
56
57            if (disposing)
58            {
59                thread.Abort();
60                schedulerInterrupt.Set();
61
62                //Wait for the executor thread to exit -- we call some event functions
63                //and these events may need invocation on the main thread. So,
64                //pump messages from the main thread until the thread exits.
65                if (System.Windows.Forms.Application.MessageLoop)
66                {
67                    if (!thread.Join(new TimeSpan(0, 0, 0, 0, 100)))
68                        System.Windows.Forms.Application.DoEvents();
69                }
70
71                //If we are disposing on a secondary thread, or a thread without
72                //a message loop, just wait for the thread to exit indefinitely
73                else
74                    thread.Join();
75
76                schedulerInterrupt.Close();
77            }
78
79            thread = null;
80            schedulerInterrupt = null;
81            base.Dispose(disposing);
82        }
83
84        public override void Run()
85        {
86            thread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
87            thread.Start();
88        }
89
90        public override void QueueTask(Task task)
91        {
92            lock (tasksLock)
93            {
94                //Queue the task to be run immediately.
95                DateTime executionTime = DateTime.Now;
96                if (!scheduledTasks.ContainsKey(executionTime))
97                    scheduledTasks.Add(executionTime, new List<Task>());
98                scheduledTasks[executionTime].Add(task);
99                schedulerInterrupt.Set();
100            }
101        }
102
103        public override void ScheduleTask(Task task)
104        {
105            RecurringSchedule schedule = task.Schedule as RecurringSchedule;
106            if (schedule == null)
107                return;
108
109            DateTime executionTime = (schedule.MissedPreviousSchedule &&
110                ManagerLibrary.Settings.ExecuteMissedTasksImmediately) ?
111                    DateTime.Now : schedule.NextRun;
112
113            lock (tasksLock)
114            {
115                if (!scheduledTasks.ContainsKey(executionTime))
116                    scheduledTasks.Add(executionTime, new List<Task>());
117                scheduledTasks[executionTime].Add(task);
118            }
119        }
120
121        public override void QueueRestartTasks()
122        {
123            lock (tasksLock)
124            {
125                foreach (Task task in Tasks)
126                    if (task.Schedule == Schedule.RunOnRestart)
127                        QueueTask(task);
128            }
129        }
130
131        public override void UnqueueTask(Task task)
132        {
133            lock (tasksLock)
134                for (int i = 0; i != scheduledTasks.Count; ++i)
135                    for (int j = 0; j < scheduledTasks.Values[i].Count; )
136                    {
137                        Task currentTask = scheduledTasks.Values[i][j];
138                        if (currentTask == task &&
139                            (!(currentTask.Schedule is RecurringSchedule) ||
140                                ((RecurringSchedule)currentTask.Schedule).NextRun != scheduledTasks.Keys[i]))
141                        {
142                            scheduledTasks.Values[i].RemoveAt(j);
143                        }
144                        else
145                        {
146                            ++j;
147                        }
148                    }
149        }
150
151        internal override bool IsTaskQueued(Task task)
152        {
153            lock (tasksLock)
154                foreach (KeyValuePair<DateTime, List<Task>> tasks in scheduledTasks)
155                    foreach (Task i in tasks.Value)
156                        if (task == i)
157                            if (task.Schedule is RecurringSchedule)
158                            {
159                                if (((RecurringSchedule)task.Schedule).NextRun != tasks.Key)
160                                    return true;
161                            }
162                            else
163                                return true;
164
165            return false;
166        }
167
168        private void OnTaskAdded(object sender, TaskEventArgs e)
169        {
170            e.Task.TaskEdited += OnTaskEdited;
171        }
172
173        private void OnTaskEdited(object sender, EventArgs e)
174        {
175            //Find all schedule entries containing the task - since the user cannot make
176            //edits to the task when it is queued (only if it is scheduled) remove
177            //all task references and add them back
178            Task task = (Task)sender;
179            lock (tasksLock)
180                for (int i = 0; i != scheduledTasks.Count; ++i)
181                    for (int j = 0; j < scheduledTasks.Values[i].Count; )
182                    {
183                        Task currentTask = scheduledTasks.Values[i][j];
184                        if (currentTask == task)
185                            scheduledTasks.Values[i].RemoveAt(j);
186                        else
187                            j++;
188                    }
189
190            //Then reschedule the task
191            if (task.Schedule is RecurringSchedule)
192                ScheduleTask(task);
193        }
194
195        private void OnTaskDeleted(object sender, TaskEventArgs e)
196        {
197            e.Task.TaskEdited -= OnTaskEdited;
198        }
199
200        public override ExecutorTasksCollection Tasks
201        {
202            get
203            {
204                return tasks;
205            }
206        }
207
208        /// <summary>
209        /// The thread entry point for this object. This object operates on a queue
210        /// and hence the thread will sequentially execute tasks.
211        /// </summary>
212        private void Main()
213        {
214            //The waiting thread will utilize a polling loop to check for new
215            //scheduled tasks. This will be checked every 30 seconds. However,
216            //when the thread is waiting for a new task, it can be interrupted.
217            while (thread.ThreadState != ThreadState.AbortRequested)
218            {
219                //Check for a new task
220                Task task = null;
221                lock (tasksLock)
222                {
223                    while (scheduledTasks.Count != 0)
224                        if (scheduledTasks.Values[0].Count == 0)
225                        {
226                            //Clean all all time slots at the start of the queue which are
227                            //empty
228                            scheduledTasks.RemoveAt(0);
229                        }
230                        else
231                        {
232                            if (scheduledTasks.Keys[0] <= DateTime.Now)
233                            {
234                                List<Task> tasks = scheduledTasks.Values[0];
235                                task = tasks[0];
236                                tasks.RemoveAt(0);
237                            }
238
239                            //Do schedule queue maintenance: clean up all empty timeslots
240                            if (task == null)
241                            {
242                                for (int i = 0; i < scheduledTasks.Count; )
243                                    if (scheduledTasks.Values[i].Count == 0)
244                                        scheduledTasks.RemoveAt(i);
245                                    else
246                                        ++i;
247                            }
248
249                            break;
250                        }
251                }
252
253                if (task != null)
254                {
255                    //Start a new log session to separate this session's events
256                    //from previous ones.
257                    LogSink sessionLog = new LogSink();
258                    task.Log.Add(sessionLog);
259                    using (new LogSession(sessionLog))
260                    {
261                        ExecuteTask(task);
262                    }
263                }
264
265                //Wait for half a minute to check for the next scheduled task.
266                schedulerInterrupt.WaitOne(30000, false);
267            }
268        }
269
270        /// <summary>
271        /// Executes the given task.
272        /// </summary>
273        /// <param name="task">The task to execute.</param>
274        private void ExecuteTask(Task task)
275        {
276            //Set the currently executing task.
277            currentTask = task;
278
279            //Prevent the system from sleeping.
280            Power.ExecutionState = ExecutionState.Continuous | ExecutionState.SystemRequired;
281
282            try
283            {
284                //Broadcast the task started event.
285                task.Canceled = false;
286                task.OnTaskStarted();
287
288                //Run the task
289                foreach (ErasureTarget target in task.Targets)
290                    try
291                    {
292                        target.Execute();
293                    }
294                    catch (FatalException)
295                    {
296                        throw;
297                    }
298                    catch (OperationCanceledException)
299                    {
300                        throw;
301                    }
302                    catch (SharingViolationException)
303                    {
304                    }
305            }
306            catch (FatalException e)
307            {
308                Logger.Log(e.Message, LogLevel.Fatal);
309            }
310            catch (OperationCanceledException e)
311            {
312                Logger.Log(e.Message, LogLevel.Fatal);
313            }
314            catch (SharingViolationException)
315            {
316            }
317            finally
318            {
319                //Allow the system to sleep again.
320                Power.ExecutionState = ExecutionState.Continuous;
321
322                //If the task is a recurring task, reschedule it since we are done.
323                if (task.Schedule is RecurringSchedule)
324                {
325                    ((RecurringSchedule)task.Schedule).Reschedule(DateTime.Now);
326                    ScheduleTask(task);
327                }
328
329                //And the task finished event.
330                task.OnTaskFinished();
331
332                //If the task is an execute on restart task or run immediately task, it is
333                //only run once and can now be restored to a manually run task
334                if (task.Schedule == Schedule.RunOnRestart || task.Schedule == Schedule.RunNow)
335                    task.Schedule = Schedule.RunManually;
336
337                //Remove the actively executing task from our instance variable
338                currentTask = null;
339            }
340        }
341
342        /// <summary>
343        /// The thread object.
344        /// </summary>
345        private Thread thread;
346
347        /// <summary>
348        /// The lock preventing concurrent access for the tasks list and the
349        /// tasks queue.
350        /// </summary>
351        private object tasksLock = new object();
352
353        /// <summary>
354        /// The queue of tasks. This queue is executed when the first element's
355        /// timestamp (the key) has been past. This list assumes that all tasks
356        /// are sorted by timestamp, smallest one first.
357        /// </summary>
358        private SortedList<DateTime, List<Task>> scheduledTasks =
359            new SortedList<DateTime, List<Task>>();
360
361        /// <summary>
362        /// The task list associated with this executor instance.
363        /// </summary>
364        private DirectExecutorTasksCollection tasks;
365
366        /// <summary>
367        /// The currently executing task.
368        /// </summary>
369        Task currentTask;
370
371        /// <summary>
372        /// An automatically reset event allowing the addition of new tasks to
373        /// interrupt the thread's sleeping state waiting for the next recurring
374        /// task to be due.
375        /// </summary>
376        AutoResetEvent schedulerInterrupt = new AutoResetEvent(true);
377
378        private class DirectExecutorTasksCollection : ExecutorTasksCollection
379        {
380            /// <summary>
381            /// Constructor.
382            /// </summary>
383            /// <param name="executor">The <see cref="DirectExecutor"/> object owning
384            /// this list.</param>
385            public DirectExecutorTasksCollection(DirectExecutor executor)
386                : base(executor)
387            {
388            }
389
390            #region IList<Task> Members
391            public override int IndexOf(Task item)
392            {
393                return list.IndexOf(item);
394            }
395
396            public override void Insert(int index, Task item)
397            {
398                item.Executor = Owner;
399                lock (list)
400                    list.Insert(index, item);
401
402                //Call all the event handlers who registered to be notified of tasks
403                //being added.
404                Owner.OnTaskAdded(new TaskEventArgs(item));
405
406                //If the task is scheduled to run now, break the waiting thread and
407                //run it immediately
408                if (item.Schedule == Schedule.RunNow)
409                {
410                    Owner.QueueTask(item);
411                }
412                //If the task is scheduled, add the next execution time to the list
413                //of schduled tasks.
414                else if (item.Schedule != Schedule.RunOnRestart)
415                {
416                    Owner.ScheduleTask(item);
417                }
418            }
419
420            public override void RemoveAt(int index)
421            {
422                lock (list)
423                {
424                    Task task = list[index];
425                    task.Cancel();
426                    task.Executor = null;
427                    list.RemoveAt(index);
428
429                    //Call all event handlers registered to be notified of task deletions.
430                    Owner.OnTaskDeleted(new TaskEventArgs(task));
431                }
432            }
433
434            public override Task this[int index]
435            {
436                get
437                {
438                    lock (list)
439                        return list[index];
440                }
441                set
442                {
443                    lock (list)
444                        list[index] = value;
445                }
446            }
447            #endregion
448
449            #region ICollection<Task> Members
450            public override void Add(Task item)
451            {
452                Insert(Count, item);
453            }
454
455            public override void Clear()
456            {
457                foreach (Task task in list)
458                    Remove(task);
459            }
460
461            public override bool Contains(Task item)
462            {
463                lock (list)
464                    return list.Contains(item);
465            }
466
467            public override void CopyTo(Task[] array, int arrayIndex)
468            {
469                lock (list)
470                    list.CopyTo(array, arrayIndex);
471            }
472
473            public override int Count
474            {
475                get
476                {
477                    lock (list)
478                        return list.Count;
479                }
480            }
481
482            public override bool Remove(Task item)
483            {
484                lock (list)
485                {
486                    int index = list.IndexOf(item);
487                    if (index < 0)
488                        return false;
489
490                    RemoveAt(index);
491                }
492
493                return true;
494            }
495            #endregion
496
497            #region IEnumerable<Task> Members
498            public override IEnumerator<Task> GetEnumerator()
499            {
500                return list.GetEnumerator();
501            }
502            #endregion
503
504            public override void SaveToStream(Stream stream)
505            {
506                lock (list)
507                    new BinaryFormatter().Serialize(stream, list);
508            }
509
510            public override void LoadFromStream(Stream stream)
511            {
512                //Load the list into the dictionary
513                StreamingContext context = new StreamingContext(
514                    StreamingContextStates.All, Owner);
515                BinaryFormatter formatter = new BinaryFormatter(null, context);
516
517                try
518                {
519                    List<Task> deserialised = (List<Task>)formatter.Deserialize(stream);
520                    list.AddRange(deserialised);
521
522                    foreach (Task task in deserialised)
523                    {
524                        Owner.OnTaskAdded(new TaskEventArgs(task));
525                        if (task.Schedule == Schedule.RunNow)
526                            Owner.QueueTask(task);
527                        else if (task.Schedule is RecurringSchedule)
528                            Owner.ScheduleTask(task);
529                    }
530                }
531                catch (FileLoadException e)
532                {
533                    throw new InvalidDataException(e.Message, e);
534                }
535                catch (SerializationException e)
536                {
537                    throw new InvalidDataException(e.Message, e);
538                }
539            }
540
541            /// <summary>
542            /// The data store for this object.
543            /// </summary>
544            private List<Task> list = new List<Task>();
545        }
546    }
547}
Note: See TracBrowser for help on using the repository browser.