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

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