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

Revision 2344, 13.3 KB checked in by lowjoel, 3 years ago (diff)

Synchronise the Plugins Rewrite branch with trunk@r2341

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