source: branches/eraser6/SpeedMeter/Eraser.Manager/ProgressManager.cs @ 1510

Revision 1510, 14.4 KB checked in by lowjoel, 4 years ago (diff)

Thread-safe the ChainedProgressManager? classes by locking the task list before modifying or accessing them. This may introduce lock contention performance penalties but we'll stick with this first (fix it with a writer lock afterward if it turns out to be a performance bottleneck.) The ProgressManager? class is not locked at all since it deals with integers only.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2009 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.Linq;
25using System.Text;
26
27namespace Eraser.Manager
28{
29    /// <summary>
30    /// Manages the progress for any operation.
31    /// </summary>
32    public abstract class ProgressManagerBase
33    {
34        /// <summary>
35        /// Constructor.
36        ///
37        /// This sets the starting time of this task to allow the computation of
38        /// the estimated end time by extrapolating collected data based on the
39        /// amount of time already elapsed.
40        /// </summary>
41        public ProgressManagerBase()
42        {
43            StartTime = DateTime.Now;
44        }
45
46        /// <summary>
47        /// Resets the starting time of the task. The speed measurement is
48        /// automatically started when the ProgressManagerBase object is created.
49        /// </summary>
50        public void Restart()
51        {
52            StartTime = DateTime.Now;
53        }
54
55        /// <summary>
56        /// Gets the percentage of the operation completed.
57        /// </summary>
58        public abstract float Progress
59        {
60            get;
61        }
62
63        /// <summary>
64        /// Computes the speed of the erase, in units of completion per second,
65        /// based on the information collected in the previous 15 seconds.
66        /// </summary>
67        public abstract int Speed
68        {
69            get;
70        }
71
72        /// <summary>
73        /// Calculates the estimated amount of time left based on the total
74        /// amount of information to erase and the current speed of the erase
75        /// </summary>
76        public abstract TimeSpan TimeLeft
77        {
78            get;
79        }
80
81        /// <summary>
82        /// The starting time of this task.
83        /// </summary>
84        public DateTime StartTime
85        {
86            get;
87            private set;
88        }
89    }
90
91    /// <summary>
92    /// Manages progress based only on one input, set through the Completed and Total
93    /// properties.
94    /// </summary>
95    public class ProgressManager : ProgressManagerBase
96    {
97        /// <summary>
98        /// Gets or sets the number of work units already completed.
99        /// </summary>
100        public long Completed
101        {
102            get;
103            set;
104        }
105
106        /// <summary>
107        /// Gets or sets the total number of work units that this task has.
108        /// </summary>
109        public long Total
110        {
111            get;
112            set;
113        }
114
115        public override float Progress
116        {
117            get
118            {
119                if (Total == 0)
120                    return 0.0f;
121
122                return (float)((double)Completed / Total);
123            }
124        }
125
126        public override int Speed
127        {
128            get
129            {
130                if (DateTime.Now == StartTime)
131                    return 0;
132
133                if ((DateTime.Now - lastSpeedCalc).Seconds < 5 && lastSpeed != 0)
134                    return lastSpeed;
135
136                //Calculate how much time has passed
137                double timeElapsed = (DateTime.Now - lastSpeedCalc).TotalSeconds;
138                if (timeElapsed == 0.0)
139                    return 0;
140
141                //Then compute the speed of the calculation
142                lastSpeed = (int)((Completed - lastCompleted) / timeElapsed);
143                lastSpeedCalc = DateTime.Now;
144                lastCompleted = Completed;
145                return lastSpeed;
146            }
147        }
148
149        public override TimeSpan TimeLeft
150        {
151            get
152            {
153                if (Speed == 0)
154                    return TimeSpan.Zero;
155                return new TimeSpan(0, 0, (int)((Total - Completed) / Speed));
156            }
157        }
158
159        /// <summary>
160        /// The last time a speed calculation was computed so that speed is not
161        /// computed too often.
162        /// </summary>
163        private DateTime lastSpeedCalc;
164
165        /// <summary>
166        /// The amount of the operation completed at the last speed computation.
167        /// </summary>
168        private long lastCompleted;
169
170        /// <summary>
171        /// The last calculated speed of the operation.
172        /// </summary>
173        private int lastSpeed;
174    }
175
176    /// <summary>
177    /// Manages progress based on sub-tasks.
178    /// </summary>
179    public abstract class ChainedProgressManager : ProgressManagerBase
180    {
181    }
182
183    /// <summary>
184    /// Manages progress based on sub-tasks, taking each sub-task to be a step
185    /// in which the next step will not be executed until the current step is
186    /// complete. Each step is also assign weights so that certain steps which
187    /// take more time are given a larger amount of progress-bar space for finer
188    /// grained progress reporting.
189    /// </summary>
190    public class SteppedProgressManager : ChainedProgressManager
191    {
192        /// <summary>
193        /// Represents one step in the list of steps to complete.
194        /// </summary>
195        public class Step
196        {
197            /// <summary>
198            /// Constructor.
199            /// </summary>
200            /// <param name="progress">The <see cref="ProgressManagerBase"/> instance
201            /// which measures the progress of this step.</param>
202            /// <param name="weight">The weight of this step. The weight is a decimal
203            /// number in the range [0.0, 1.0] which represents the percentage of the
204            /// entire process this particular step is.</param>
205            public Step(ProgressManagerBase progress, float weight)
206                : this(progress, weight, null)
207            {
208            }
209
210            /// <summary>
211            /// Constructor.
212            /// </summary>
213            /// <param name="progress">The <see cref="ProgressManagerBase"/> instance
214            /// which measures the progress of this step.</param>
215            /// <param name="weight">The weight of this step. The weight is a decimal
216            /// number in the range [0.0, 1.0] which represents the percentage of the
217            /// entire process this particular step is.</param>
218            /// <param name="name">A user-specified value of the name of this step.
219            /// This value is not used by the class at all.</param>
220            public Step(ProgressManagerBase progress, float weight, string name)
221            {
222                Progress = progress;
223                Weight = weight;
224                Name = name;
225            }
226
227            /// <summary>
228            /// The <see cref="ProgressManagerBase"/> instance which measures the
229            /// progress of the step.
230            /// </summary>
231            public ProgressManagerBase Progress
232            {
233                get;
234                set;
235            }
236
237            /// <summary>
238            /// The weight associated with this step.
239            /// </summary>
240            public float Weight
241            {
242                get;
243                private set;
244            }
245
246            /// <summary>
247            /// The name of this step.
248            /// </summary>
249            public string Name
250            {
251                get;
252                set;
253            }
254        }
255
256        /// <summary>
257        /// The class which manages the steps which comprise the overall progress.
258        /// </summary>
259        public class StepsList : IList<Step>
260        {
261            public StepsList(SteppedProgressManager manager)
262            {
263                List = new List<Step>();
264                ListLock = manager.ListLock;
265                Manager = manager;
266            }
267
268            #region IList<Step> Members
269
270            public int IndexOf(Step item)
271            {
272                lock (ListLock)
273                    return List.IndexOf(item);
274            }
275
276            public void Insert(int index, Step item)
277            {
278                lock (ListLock)
279                {
280                    List.Insert(index, item);
281                    TotalWeights += item.Weight;
282                }
283            }
284
285            public void RemoveAt(int index)
286            {
287                lock (ListLock)
288                {
289                    TotalWeights -= List[index].Weight;
290                    List.RemoveAt(index);
291                }
292            }
293
294            public Step this[int index]
295            {
296                get
297                {
298                    lock (ListLock)
299                        return List[index];
300                }
301                set
302                {
303                    lock (ListLock)
304                    {
305                        TotalWeights -= List[index].Weight;
306                        List[index] = value;
307                        TotalWeights += value.Weight;
308                    }
309                }
310            }
311
312            #endregion
313
314            #region ICollection<Step> Members
315
316            public void Add(Step item)
317            {
318                lock (ListLock)
319                {
320                    List.Add(item);
321                    TotalWeights += item.Weight;
322                }
323            }
324
325            public void Clear()
326            {
327                lock (ListLock)
328                {
329                    List.Clear();
330                    TotalWeights = 0;
331                }
332            }
333
334            public bool Contains(Step item)
335            {
336                lock (ListLock)
337                    return List.Contains(item);
338            }
339
340            public void CopyTo(Step[] array, int arrayIndex)
341            {
342                lock (ListLock)
343                    List.CopyTo(array, arrayIndex);
344            }
345
346            public int Count
347            {
348                get
349                {
350                    lock (ListLock) 
351                        return List.Count;
352                }
353            }
354
355            public bool IsReadOnly
356            {
357                get { return false; }
358            }
359
360            public bool Remove(Step item)
361            {
362                int index = List.IndexOf(item);
363                if (index != -1)
364                    TotalWeights -= List[index].Weight;
365
366                return List.Remove(item);
367            }
368
369            #endregion
370
371            #region IEnumerable<Step> Members
372
373            public IEnumerator<Step> GetEnumerator()
374            {
375                return List.GetEnumerator();
376            }
377
378            #endregion
379
380            #region IEnumerable Members
381
382            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
383            {
384                return List.GetEnumerator();
385            }
386
387            #endregion
388
389            /// <summary>
390            /// The total weights of all the steps.
391            /// </summary>
392            private float TotalWeights
393            {
394                get
395                {
396                    return totalWeights;
397                }
398                set
399                {
400                    if (value >= 1.1f || value < 0.0f)
401                        throw new ArgumentOutOfRangeException("The total weights of all steps in " +
402                            "the task must be within the range [0.0, 1.0]");
403
404                    totalWeights = value;
405                }
406            }
407
408            /// <summary>
409            /// The list storing the steps for this instance.
410            /// </summary>
411            private List<Step> List;
412
413            /// <summary>
414            /// The lock object guarding the list against parallel writes.
415            /// </summary>
416            private object ListLock;
417
418            /// <summary>
419            /// The <see cref="SteppedProgressManager"/> instance which owns this list.
420            /// </summary>
421            private SteppedProgressManager Manager;
422
423            /// <summary>
424            /// The backing variable for the total weights of all the steps.
425            /// </summary>
426            private float totalWeights;
427        }
428
429        /// <summary>
430        /// Constructor.
431        /// </summary>
432        public SteppedProgressManager()
433        {
434            Steps = new StepsList(this);
435            ListLock = new object();
436        }
437
438        public override float Progress
439        {
440            get
441            {
442                float result = 0.0f;
443                lock (ListLock)
444                    foreach (Step step in Steps)
445                        result += step.Progress.Progress * step.Weight;
446
447                return result;
448            }
449        }
450
451        public override int Speed
452        {
453            get
454            {
455                if (CurrentStep == null)
456                    return 0;
457
458                return CurrentStep.Progress.Speed;
459            }
460        }
461
462        public override TimeSpan TimeLeft
463        {
464            get
465            {
466                long ticksElapsed = (DateTime.Now - StartTime).Ticks;
467                float progressRemaining = 1.0f - Progress;
468                return new TimeSpan((long)
469                    (progressRemaining * (ticksElapsed / (double)Progress)));
470            }
471        }
472
473        /// <summary>
474        /// The list of steps involved in completion of the task.
475        /// </summary>
476        public StepsList Steps
477        {
478            get;
479            private set;
480        }
481
482        /// <summary>
483        /// Gets the current step which is executing. This property is null if
484        /// no steps are executing (also when the task is complete)
485        /// </summary>
486        public Step CurrentStep
487        {
488            get
489            {
490                lock (ListLock)
491                {
492                    if (Steps.Count == 0)
493                        return null;
494
495                    foreach (Step step in Steps)
496                        if (step.Progress.Progress < 1.0f)
497                            return step;
498
499                    //Return the last step since we don't have any
500                    return Steps[Steps.Count - 1];
501                }
502            }
503        }
504
505        /// <summary>
506        /// The lock object guarding the list of steps against concurrent read and write.
507        /// </summary>
508        private object ListLock;
509    }
510
511    /// <summary>
512    /// Manages progress based on sub-tasks, assuming each sub-task to be independent
513    /// of the rest.
514    /// </summary>
515    public class ParallelProgressManager : ChainedProgressManager
516    {
517        /// <summary>
518        /// The class which manages the progress of each dependent task.
519        /// </summary>
520        public class SubTasksList : IList<ProgressManagerBase>
521        {
522            public SubTasksList(ParallelProgressManager manager)
523            {
524                List = new List<ProgressManagerBase>();
525                ListLock = manager.TaskLock;
526            }
527
528            #region IList<SubTasksList> Members
529
530            public int IndexOf(ProgressManagerBase item)
531            {
532                lock (ListLock)
533                    return List.IndexOf(item);
534            }
535
536            public void Insert(int index, ProgressManagerBase item)
537            {
538                lock (ListLock)
539                    List.Insert(index, item);
540            }
541
542            public void RemoveAt(int index)
543            {
544                lock (ListLock)
545                    List.RemoveAt(index);
546            }
547
548            public ProgressManagerBase this[int index]
549            {
550                get
551                {
552                    lock (ListLock)
553                        return List[index];
554                }
555                set
556                {
557                    lock (ListLock)
558                        List[index] = value;
559                }
560            }
561
562            #endregion
563
564            #region ICollection<SteppedProgressManagerStep> Members
565
566            public void Add(ProgressManagerBase item)
567            {
568                lock (ListLock)
569                    List.Add(item);
570            }
571
572            public void Clear()
573            {
574                lock (ListLock)
575                    List.Clear();
576            }
577
578            public bool Contains(ProgressManagerBase item)
579            {
580                return List.Contains(item);
581            }
582
583            public void CopyTo(ProgressManagerBase[] array, int arrayIndex)
584            {
585                lock (ListLock)
586                    List.CopyTo(array, arrayIndex);
587            }
588
589            public int Count
590            {
591                get
592                {
593                    lock (ListLock) 
594                        return List.Count;
595                }
596            }
597
598            public bool IsReadOnly
599            {
600                get { return false; }
601            }
602
603            public bool Remove(ProgressManagerBase item)
604            {
605                lock (ListLock)
606                    return List.Remove(item);
607            }
608
609            #endregion
610
611            #region IEnumerable<ProgressManagerBase> Members
612
613            public IEnumerator<ProgressManagerBase> GetEnumerator()
614            {
615                return List.GetEnumerator();
616            }
617
618            #endregion
619
620            #region IEnumerable Members
621
622            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
623            {
624                return List.GetEnumerator();
625            }
626
627            #endregion
628
629            /// <summary>
630            /// The list storing the steps for this instance.
631            /// </summary>
632            private List<ProgressManagerBase> List;
633
634            /// <summary>
635            /// The lock object guarding the list from concurrent read/writes.
636            /// </summary>
637            private object ListLock;
638        }
639
640        /// <summary>
641        /// Constructor.
642        /// </summary>
643        public ParallelProgressManager()
644        {
645            Tasks = new SubTasksList(this);
646            TaskLock = new object();
647        }
648
649        public override float Progress
650        {
651            get
652            {
653                float result = 0.0f;
654                lock (TaskLock)
655                    foreach (ProgressManagerBase subTask in Tasks)
656                        result += subTask.Progress * (1.0f / Tasks.Count);
657
658                return result;
659            }
660        }
661
662        public override int Speed
663        {
664            get
665            {
666                int maxSpeed = 0;
667                lock (TaskLock)
668                    foreach (ProgressManagerBase subTask in Tasks)
669                        maxSpeed = Math.Max(subTask.Speed, maxSpeed);
670
671                return maxSpeed;
672            }
673        }
674
675        public override TimeSpan TimeLeft
676        {
677            get
678            {
679                TimeSpan maxTime = TimeSpan.MinValue;
680                lock (TaskLock)
681                    foreach (ProgressManagerBase subTask in Tasks)
682                        if (maxTime < subTask.TimeLeft)
683                            maxTime = subTask.TimeLeft;
684
685                return maxTime;
686            }
687        }
688
689        /// <summary>
690        /// Gets the list of tasks which must complete execution before the task
691        /// is completed.
692        /// </summary>
693        public SubTasksList Tasks
694        {
695            get;
696            private set;
697        }
698
699        /// <summary>
700        /// The lock object guarding the list of tasks against concurrent read and write.
701        /// </summary>
702        private object TaskLock;
703    }
704}
Note: See TracBrowser for help on using the repository browser.