source: branches/eraser6/pluginsRewrite/Eraser.Manager/Task.cs @ 2488

Revision 2488, 9.9 KB checked in by lowjoel, 3 years ago (diff)

Change the progress updates to be a pull paradigm and not a push paradigm: this reduces the amount of time the CPU spends sending progress feedback, at the same time, allows future extensibility for decoupling the task executor from the front end.

  • 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:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25using System.IO;
26using System.Runtime.Serialization;
27using System.Security.Permissions;
28using System.Threading;
29
30using Eraser.Util;
31using Eraser.Util.ExtensionMethods;
32using Eraser.Plugins;
33using Eraser.Plugins.ExtensionPoints;
34
35namespace Eraser.Manager
36{
37    /// <summary>
38    /// Deals with an erase task.
39    /// </summary>
40    [Serializable]
41    public class Task : ITask, ISerializable
42    {
43        #region Serialization code
44        protected Task(SerializationInfo info, StreamingContext context)
45        {
46            Name = (string)info.GetValue("Name", typeof(string));
47            Executor = context.Context as Executor;
48            Targets = (ErasureTargetCollection)info.GetValue("Targets", typeof(ErasureTargetCollection));
49            Targets.Owner = this;
50            Log = (List<LogSink>)info.GetValue("Log", typeof(List<LogSink>));
51            Canceled = false;
52
53            Schedule schedule = (Schedule)info.GetValue("Schedule", typeof(Schedule));
54            if (schedule.GetType() == Schedule.RunManually.GetType())
55                Schedule = Schedule.RunManually;
56            else if (schedule.GetType() == Schedule.RunNow.GetType())
57                Schedule = Schedule.RunNow;
58            else if (schedule.GetType() == Schedule.RunOnRestart.GetType())
59                Schedule = Schedule.RunOnRestart;
60            else if (schedule is RecurringSchedule)
61                Schedule = schedule;
62            else
63                throw new InvalidDataException(S._("An invalid type was found when loading " +
64                    "the task schedule"));
65        }
66
67        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
68        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
69        {
70            info.AddValue("Name", Name);
71            info.AddValue("Schedule", Schedule);
72            info.AddValue("Targets", Targets);
73            info.AddValue("Log", Log);
74        }
75        #endregion
76
77        /// <summary>
78        /// Constructor.
79        /// </summary>
80        public Task()
81        {
82            Name = string.Empty;
83            Targets = new ErasureTargetCollection(this);
84            Schedule = Schedule.RunNow;
85            Canceled = false;
86            Log = new List<LogSink>();
87        }
88
89        /// <summary>
90        /// The Executor object which is managing this task.
91        /// </summary>
92        public Executor Executor
93        {
94            get
95            {
96                return executor;
97            }
98            internal set
99            {
100                if (value == null)
101                    throw new ArgumentNullException();
102                if (executor != null)
103                    throw new InvalidOperationException();
104
105                executor = value;
106            }
107        }
108
109        /// <summary>
110        /// The name for this task. This is just an opaque value for the user to
111        /// recognize the task.
112        /// </summary>
113        public string Name { get; set; }
114
115        /// <summary>
116        /// The name of the task, used for display in UI elements.
117        /// </summary>
118        public override string ToString()
119        {
120            //Simple case, the task name was given by the user.
121            if (!string.IsNullOrEmpty(Name))
122                return Name;
123
124            string result = string.Empty;
125            if (Targets.Count == 0)
126                return result;
127            else if (Targets.Count < 5)
128            {
129                //Simpler case, small set of data.
130                foreach (IErasureTarget tgt in Targets)
131                    result += S._("{0}, ", tgt);
132
133                return result.Remove(result.Length - 2);
134            }
135            else
136            {
137                //Ok, we've quite a few entries, get the first, the mid and the end.
138                result = S._("{0}, ", Targets[0]);
139                result += S._("{0}, ", Targets[Targets.Count / 2]);
140                result += Targets[Targets.Count - 1];
141
142                return S._("{0} and {1} other targets", result, Targets.Count - 3);
143            }
144        }
145
146        /// <summary>
147        /// The set of data to erase when this task is executed.
148        /// </summary>
149        public ErasureTargetCollection Targets { get; private set; }
150
151        /// <summary>
152        /// <see cref="Targets"/>
153        /// </summary>
154        ICollection<IErasureTarget> ITask.Targets
155        {
156            get { return Targets; }
157        }
158
159        /// <summary>
160        /// The schedule for running the task.
161        /// </summary>
162        public Schedule Schedule
163        {
164            get
165            {
166                return schedule;
167            }
168            set
169            {
170                if (value.Owner != null)
171                    throw new ArgumentException("The schedule provided can only " +
172                        "belong to one task at a time");
173
174                if (schedule is RecurringSchedule)
175                    ((RecurringSchedule)schedule).Owner = null;
176                schedule = value;
177                if (schedule is RecurringSchedule)
178                    ((RecurringSchedule)schedule).Owner = this;
179                OnTaskEdited();
180            }
181        }
182
183        /// <summary>
184        /// The log entries which this task has accumulated.
185        /// </summary>
186        public List<LogSink> Log { get; private set; }
187
188        /// <summary>
189        /// The progress manager object which manages the progress of this task.
190        /// </summary>
191        public SteppedProgressManager Progress
192        {
193            get
194            {
195                if (!Executing)
196                    throw new InvalidOperationException("The progress of an erasure can only " +
197                        "be queried when the task is being executed.");
198
199                return progress;
200            }
201            private set
202            {
203                progress = value;
204            }
205        }
206
207        /// <summary>
208        /// Gets the status of the task - whether it is being executed.
209        /// </summary>
210        public bool Executing { get; private set; }
211
212        /// <summary>
213        /// Gets whether this task is currently queued to run. This is true only
214        /// if the queue it is in is an explicit request, i.e will run when the
215        /// executor is idle.
216        /// </summary>
217        public bool Queued
218        {
219            get
220            {
221                if (Executor == null)
222                    throw new InvalidOperationException();
223
224                return Executor.IsTaskQueued(this);
225            }
226        }
227
228        /// <summary>
229        /// Gets whether the task has been cancelled from execution.
230        /// </summary>
231        public bool Canceled
232        {
233            get;
234            private set;
235        }
236
237        /// <summary>
238        /// Cancels the task from running, or, if the task is queued for running,
239        /// removes the task from the queue.
240        /// </summary>
241        public void Cancel()
242        {
243            Executor.UnqueueTask(this);
244            Canceled = true;
245        }
246
247        /// <summary>
248        /// Executes the task in the context of the calling thread.
249        /// </summary>
250        public void Execute()
251        {
252            OnTaskStarted();
253            Executing = true;
254            Canceled = false;
255            Progress = new SteppedProgressManager();
256
257            try
258            {
259                //Run the task
260                foreach (IErasureTarget target in Targets)
261                    try
262                    {
263                        Progress.Steps.Add(new ErasureTargetProgressManagerStep(
264                            target, Targets.Count));
265                        target.Execute();
266                    }
267                    catch (FatalException)
268                    {
269                        throw;
270                    }
271                    catch (OperationCanceledException)
272                    {
273                        throw;
274                    }
275                    catch (SharingViolationException)
276                    {
277                    }
278            }
279            catch (FatalException e)
280            {
281                Logger.Log(e.Message, LogLevel.Fatal);
282            }
283            catch (OperationCanceledException e)
284            {
285                Logger.Log(e.Message, LogLevel.Fatal);
286            }
287            catch (SharingViolationException)
288            {
289            }
290            finally
291            {
292                //If the task is a recurring task, reschedule it since we are done.
293                if (Schedule is RecurringSchedule)
294                {
295                    ((RecurringSchedule)Schedule).Reschedule(DateTime.Now);
296                }
297
298                //If the task is an execute on restart task or run immediately task, it is
299                //only run once and can now be restored to a manually run task
300                if (Schedule == Schedule.RunOnRestart || Schedule == Schedule.RunNow)
301                    Schedule = Schedule.RunManually;
302
303                Progress = null;
304                Executing = false;
305                OnTaskFinished();
306            }
307        }
308
309        private Executor executor;
310        private Schedule schedule;
311        private SteppedProgressManager progress;
312
313        #region Events
314        /// <summary>
315        /// The task has been edited.
316        /// </summary>
317        public EventHandler TaskEdited { get; set; }
318
319        /// <summary>
320        /// The start of the execution of a task.
321        /// </summary>
322        public EventHandler TaskStarted { get; set; }
323
324        /// <summary>
325        /// The completion of the execution of a task.
326        /// </summary>
327        public EventHandler TaskFinished { get; set; }
328
329        /// <summary>
330        /// Broadcasts the task edited event.
331        /// </summary>
332        internal void OnTaskEdited()
333        {
334            if (TaskEdited != null)
335                TaskEdited(this, EventArgs.Empty);
336        }
337
338        /// <summary>
339        /// Broadcasts the task execution start event.
340        /// </summary>
341        private void OnTaskStarted()
342        {
343            if (TaskStarted != null)
344                TaskStarted(this, EventArgs.Empty);
345        }
346
347        /// <summary>
348        /// Broadcasts the task execution completion event.
349        /// </summary>
350        private void OnTaskFinished()
351        {
352            if (TaskFinished != null)
353                TaskFinished(this, EventArgs.Empty);
354        }
355        #endregion
356    }
357
358    /// <summary>
359    /// Returns the progress of an erasure target, since that comprises the
360    /// steps of the Task Progress.
361    /// </summary>
362    public class ErasureTargetProgressManagerStep : SteppedProgressManagerStepBase
363    {
364        /// <summary>
365        /// Constructor.
366        /// </summary>
367        /// <param name="target">The erasure target represented by this object.</param>
368        /// <param name="steps">The number of targets in the task.</param>
369        public ErasureTargetProgressManagerStep(IErasureTarget target, int targets)
370            : base(1.0f / targets)
371        {
372            Target = target;
373        }
374
375        public override ProgressManagerBase Progress
376        {
377            get
378            {
379                return Target.Progress;
380            }
381            set
382            {
383                throw new InvalidOperationException();
384            }
385        }
386
387        /// <summary>
388        /// The erasure target represented by this step.
389        /// </summary>
390        public IErasureTarget Target
391        {
392            get;
393            private set;
394        }
395    }
396
397    /// <summary>
398    /// A base event class for all event arguments involving a task.
399    /// </summary>
400    public class TaskEventArgs : EventArgs
401    {
402        /// <summary>
403        /// Constructor.
404        /// </summary>
405        /// <param name="task">The task being referred to by this event.</param>
406        public TaskEventArgs(Task task)
407        {
408            Task = task;
409        }
410
411        /// <summary>
412        /// The executing task.
413        /// </summary>
414        public Task Task { get; private set; }
415    }
416}
Note: See TracBrowser for help on using the repository browser.