2005-08-16 Gabor Kelemen <kelemeng@gnome.hu>
[beagle.git] / Util / Scheduler.cs
blob97db2ffaad3ff47e0c18f4b65173f9d0dbf50df8
1 //
2 // Scheduler.cs
3 //
4 // Copyright (C) 2004 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
27 using System;
28 using System.Collections;
29 using System.Text;
30 using System.Threading;
32 namespace Beagle.Util {
34 public class Scheduler {
36 static public bool Debug = false;
38 static private double global_delay = -1.0;
39 static private bool immediate_priority_only = false;
40 static private string exercise_the_dog = null;
42 static Scheduler ()
44 // We used to support the EXERCISE_THE_DOG env variable, but
45 // that was dropped.
46 exercise_the_dog = Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG");
47 if (exercise_the_dog == null &&
48 Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG_HARDER") != null)
49 exercise_the_dog = "harder fallback";
51 if (exercise_the_dog != null) {
53 global_delay = 0.0;
55 if (exercise_the_dog.Length > 2 && exercise_the_dog [0] == 't')
56 global_delay = Double.Parse (exercise_the_dog.Substring (1));
59 if (Environment.GetEnvironmentVariable ("BEAGLE_IMMEDIATE_PRIORITY_ONLY") != null)
60 immediate_priority_only = true;
63 //////////////////////////////////////////////////////////////////////////////
65 static private Scheduler global = new Scheduler ();
67 static public Scheduler Global {
68 get { return global; }
71 //////////////////////////////////////////////////////////////////////////////
73 public enum Priority {
74 Shutdown = 0, // Do it on shutdown
75 Idle = 1, // Do it when the system is idle
76 Generator = 2, // Do it soon, but not *too* soon
77 Delayed = 3, // Do it soon
78 Immediate = 4, // Do it right now
81 public delegate void Hook ();
82 public delegate void TaskHook (Task task);
84 //////////////////////////////////////////////////////////////////////////////
86 public abstract class Task : IComparable {
88 // A unique identifier
89 public string Tag;
91 // Some metadata
92 public string Creator;
93 public string Description;
95 public Priority Priority = Priority.Idle;
96 public int SubPriority = 0;
98 public DateTime Timestamp;
99 public DateTime TriggerTime = DateTime.MinValue;
101 public ITaskCollector Collector = null;
102 public double Weight = 1.0;
104 public bool Reschedule = false;
106 ///////////////////////////////
108 private ArrayList task_groups = null;
110 public void AddTaskGroup (TaskGroup group)
112 if (task_groups == null)
113 task_groups = new ArrayList ();
114 task_groups.Add (group);
117 private void IncrementAllTaskGroups ()
119 if (task_groups != null) {
120 foreach (TaskGroupPrivate group in task_groups) {
121 if (! group.Finished)
122 group.Increment ();
127 private void DecrementAllTaskGroups ()
129 if (task_groups != null) {
130 foreach (TaskGroupPrivate group in task_groups) {
131 if (! group.Finished)
132 group.Decrement ();
137 private void TouchAllTaskGroups ()
139 if (task_groups != null) {
140 foreach (TaskGroupPrivate group in task_groups) {
141 if (! group.Finished)
142 group.Touch ();
147 ///////////////////////////////
149 private Scheduler scheduler = null;
151 public Scheduler ThisScheduler {
152 get { return scheduler; }
155 public void Schedule (Scheduler scheduler)
157 // Increment the task groups the first
158 // time a task is scheduled.
159 if (this.scheduler == null)
160 IncrementAllTaskGroups ();
161 this.scheduler = scheduler;
164 ///////////////////////////////
166 private bool cancelled = false;
168 public bool Cancelled {
169 get { return cancelled; }
172 public void Cancel ()
174 if (! cancelled)
175 DecrementAllTaskGroups ();
176 cancelled = true;
179 ///////////////////////////////
181 // The Task's count keeps track of how many
182 // times it has been executed.
184 private int count = 0;
186 public int Count {
187 get { return count; }
190 ///////////////////////////////
192 public void DoTask ()
194 if (! cancelled) {
195 TouchAllTaskGroups ();
196 try {
197 if (Debug)
198 Logger.Log.Debug ("Starting task {0}", Tag);
199 Stopwatch sw = new Stopwatch ();
200 sw.Start ();
201 DoTaskReal ();
202 sw.Stop ();
203 if (Debug)
204 Logger.Log.Debug ("Finished task {0} in {1}", Tag, sw);
205 } catch (Exception ex) {
206 Logger.Log.Warn ("Caught exception in DoTaskReal");
207 Logger.Log.Warn (" Tag: {0}", Tag);
208 Logger.Log.Warn (" Creator: {0}", Creator);
209 Logger.Log.Warn ("Description: {0}", Description);
210 Logger.Log.Warn (" Priority: {0} ({1})", Priority, SubPriority);
211 Logger.Log.Warn (ex);
213 if (Reschedule) {
214 Reschedule = false;
215 ++count;
216 ThisScheduler.Add (this);
217 } else {
218 DecrementAllTaskGroups ();
223 protected abstract void DoTaskReal ();
225 ///////////////////////////////
227 // Sort from lowest to highest priority
228 public int CompareTo (object obj)
230 Task other = obj as Task;
231 if (other == null)
232 return 1;
234 int cmp;
235 cmp = this.Priority.CompareTo (other.Priority);
236 if (cmp != 0)
237 return cmp;
239 cmp = this.SubPriority.CompareTo (other.SubPriority);
240 if (cmp != 0)
241 return cmp;
243 cmp = other.Timestamp.CompareTo (this.Timestamp);
244 if (cmp != 0)
245 return cmp;
247 // Try to break any ties
248 return this.GetHashCode ().CompareTo (other.GetHashCode ());
251 public override string ToString ()
253 StringBuilder sb = new StringBuilder ();
255 sb.AppendFormat ("{0} {1}\n", Priority, SubPriority);
257 sb.Append (Tag + "\n");
259 double t = (TriggerTime - DateTime.Now).TotalSeconds;
260 if (t > 0) {
261 if (t < 120)
262 sb.AppendFormat ("Trigger in {0:0.00} seconds\n", t);
263 else
264 sb.AppendFormat ("Trigger at {0}\n", TriggerTime);
267 if (Creator != null)
268 sb.AppendFormat ("Creator: {0}\n", Creator);
270 if (Description != null)
271 sb.Append (Description + "\n");
273 return sb.ToString ();
277 private class TaskHookWrapper : Task {
279 TaskHook hook;
281 public TaskHookWrapper (TaskHook hook)
283 this.hook = hook;
286 protected override void DoTaskReal ()
288 if (hook != null)
289 hook (this);
293 public static Task TaskFromHook (TaskHook hook)
295 return new TaskHookWrapper (hook);
298 //////////////////////////////////////////////////////////////////////////////
301 // Task Groups
304 public static TaskGroup NewTaskGroup (string name, Hook pre_hook, Hook post_hook)
306 return new TaskGroupPrivate (name, pre_hook, post_hook);
309 // We split the task group data structure into two parts:
310 // TaskGroup and TaskGroupPrivate. The TaskGroup we hand
311 // back to the user exposes minimal functionality.
312 public abstract class TaskGroup {
313 private string name;
315 protected TaskGroup (string name) {
316 this.name = name;
319 public string Name {
320 get { return name; }
323 public abstract bool Finished { get; }
326 private class TaskGroupPrivate : TaskGroup {
327 private int task_count = 0;
328 private bool touched = false;
329 private bool finished = false;
330 private Hook pre_hook;
331 private Hook post_hook;
333 public TaskGroupPrivate (string name,
334 Hook pre_hook,
335 Hook post_hook) : base (name)
337 this.pre_hook = pre_hook;
338 this.post_hook = post_hook;
341 public override bool Finished {
342 get { return finished; }
345 public void Increment ()
347 if (finished)
348 throw new Exception ("Tried to increment a finished TaskGroup");
349 ++task_count;
352 public void Touch ()
354 if (finished)
355 throw new Exception ("Tried to touch a finished TaskGroup");
357 if (! touched) {
358 if (pre_hook != null) {
359 try {
360 pre_hook ();
361 } catch (Exception ex) {
362 Logger.Log.Warn ("Caught exception in pre_hook of task group '{0}'", Name);
363 Logger.Log.Warn (ex);
366 touched = true;
370 public void Decrement ()
372 if (finished)
373 throw new Exception ("Tried to decrement a finished TaskGroup");
375 --task_count;
376 // Only fire our post-hook if the pre-hook fired
377 // (or would have fired, had it been non-null)
378 if (task_count == 0 && touched) {
379 if (post_hook != null) {
380 try {
381 post_hook ();
382 } catch (Exception ex) {
383 Logger.Log.Warn ("Caught exception in post_hook of task group '{0}'", Name);
384 Logger.Log.Warn (ex);
387 finished = true;
392 //////////////////////////////////////////////////////////////////////////////
395 // Task Collector
397 // This is a mechanism for executing tasks in sets, possibly outside of
398 // priority order.
401 public interface ITaskCollector {
403 double GetMinimumWeight ();
404 double GetMaximumWeight ();
406 void PreTaskHook ();
407 void PostTaskHook ();
410 //////////////////////////////////////////////////////////////////////////////
412 // FIXME: shutdown tasks should probably be ordered by something
413 private Queue shutdown_task_queue = new Queue ();
415 private ArrayList task_queue = new ArrayList ();
416 private Hashtable task_by_tag = new Hashtable ();
417 private int executed_task_count = 0;
419 public enum AddType {
420 DeferToExisting,
421 OptionallyReplaceExisting,
422 OnlyReplaceExisting,
423 BlockUntilNoCollision // This is very dangerous!
427 public bool Add (Task task, AddType add_type)
429 Task old_task = null;
431 lock (task_queue) {
432 if (task != null) {
434 if (immediate_priority_only && task.Priority != Priority.Immediate)
435 return false;
437 // Keep track of when immediate priority tasks are
438 // added so that we can throttle if the scheduler
439 // is being slammed with them.
440 if (task.Priority == Priority.Immediate) {
441 // Shift our times down by one
442 Array.Copy (last_immediate_times, 1, last_immediate_times, 0, 4);
443 last_immediate_times [4] = DateTime.Now;
446 old_task = task_by_tag [task.Tag] as Task;
447 if (old_task == task)
448 return true;
450 // Wait until there isn't anything in the task queue with this
451 // tag. This is EXTREMELY DANGEROUS. It could easily
452 // block for a long time, and might lead to weird deadlocks
453 // if used without the utmost of care.
454 // FIXME: If we are blocking until a task is executed, we should
455 // probably allow it to skip ahead in the queue.
456 if (add_type == AddType.BlockUntilNoCollision) {
457 while (old_task != null) {
458 Monitor.Wait (task_queue);
459 if (! running)
460 return false;
461 old_task = task_by_tag [task.Tag] as Task;
465 if (add_type == AddType.DeferToExisting
466 && old_task != null)
467 return false;
469 if (add_type == AddType.OnlyReplaceExisting
470 && old_task == null)
471 return false;
473 if (Debug) {
474 Logger.Log.Debug ("Adding task");
475 Logger.Log.Debug ("Tag: {0}", task.Tag);
476 if (task.Description != null)
477 Logger.Log.Debug ("Desc: {0}", task.Description);
480 task.Timestamp = DateTime.Now;
481 task.Schedule (this);
483 if (task.Priority == Priority.Shutdown) {
484 shutdown_task_queue.Enqueue (task);
485 } else {
486 int i = task_queue.BinarySearch (task);
487 if (i < 0)
488 i = ~i;
489 task_queue.Insert (i, task);
490 task_by_tag [task.Tag] = task;
494 Monitor.Pulse (task_queue);
497 if (old_task != null)
498 old_task.Cancel ();
500 return true;
503 public bool Add (Task task)
505 return Add (task, AddType.OptionallyReplaceExisting);
508 public Task GetByTag (string tag)
510 lock (task_queue) {
511 return task_by_tag [tag] as Task;
515 public bool ContainsByTag (string tag)
517 Task task = GetByTag (tag);
518 return task != null && !task.Cancelled;
523 //////////////////////////////////////////////////////////////////////////////
525 private string status_str = null;
526 private DateTime next_task_time;
528 public string GetHumanReadableStatus ()
530 StringBuilder sb = new StringBuilder ();
532 sb.Append ("Scheduler:\n");
534 sb.Append (String.Format ("Count: {0}\n", executed_task_count));
536 if (next_task_time.Ticks > 0)
537 sb.Append (String.Format ("Next task in {0:0.00} seconds\n",
538 (next_task_time - DateTime.Now).TotalSeconds));
540 if (status_str != null)
541 sb.Append ("Status: " + status_str + "\n");
543 lock (task_queue) {
544 int pos = 1;
545 for (int i = task_queue.Count - 1; i >= 0; --i) {
546 Task task = task_queue [i] as Task;
547 if (task == null || task.Cancelled)
548 continue;
550 sb.AppendFormat ("{0} ", pos);
551 sb.Append (task.ToString ());
552 sb.Append ("\n");
554 ++pos;
557 if (pos == 1)
558 sb.Append ("Scheduler queue is empty.\n");
561 sb.Append ("\n");
563 return sb.ToString ();
566 //////////////////////////////////////////////////////////////////////////////
568 Thread thread = null;
569 public bool running = false;
571 public void Start ()
573 lock (this) {
574 if (thread != null)
575 return;
576 running = true;
577 thread = new Thread (new ThreadStart (Worker));
578 thread.Start ();
582 public void Stop ()
584 lock (this) {
585 if (running) {
586 running = false;
587 lock (task_queue)
588 Monitor.Pulse (task_queue);
594 // Delay Computations
596 // This code controls how we space out tasks
599 // FIXME: random magic constants
600 const double idle_threshold = 5.314159 * 60; // probably should be longer
601 const double idle_ramp_up_time = 5.271828 * 60; // probably should be longer
602 const double default_delayed_rate_factor = 9.03; // work about 1/10th of the time
603 const double default_idle_rate_factor = 2.097; // work about 1/3rd of the time
604 const double default_maximum_delay = 20; // never wait for more than 20s
606 DateTime[] last_immediate_times = new DateTime [5];
608 private double GetIdleTime ()
610 return SystemInformation.InputIdleTime;
613 // The return value and duration_of_previous_task are both measured in seconds.
614 private double ComputeDelay (Priority priority_of_next_task,
615 double duration_of_previous_task)
617 if (global_delay >= 0.0)
618 return global_delay;
620 double rate_factor;
622 rate_factor = 2.0;
624 // Do everything faster the longer we are idle.
625 double idle_time = GetIdleTime ();
626 double idle_scale = 1.0;
627 bool is_idle = false;
628 bool need_throttle = false;
630 // Never speed up if we are using the battery.
631 if (idle_time > idle_threshold && ! SystemInformation.UsingBattery) {
632 is_idle = true;
633 double t = (idle_time - idle_threshold) / idle_ramp_up_time;
634 idle_scale = (1 - Math.Min (t, 1.0));
637 switch (priority_of_next_task) {
639 case Priority.Immediate:
640 rate_factor = 0;
642 if (last_immediate_times [0] != DateTime.MinValue) {
643 TimeSpan last_add_delta = DateTime.Now.Subtract (last_immediate_times [4]);
645 // If less than a second has gone by since the
646 // last immediate task was added, there is
647 // still a torrent of events coming in, and we
648 // may need to throttle.
649 if (last_add_delta.Seconds <= 1) {
650 TimeSpan between_add_delta = last_immediate_times [4].Subtract (last_immediate_times [0]);
652 // At least 5 immediate tasks have been
653 // added in the last second. We
654 // definitely need to throttle.
655 if (between_add_delta.Seconds <= 1) {
656 Logger.Log.Debug ("Thottling immediate priority tasks");
657 need_throttle = true;
658 rate_factor = idle_scale * default_idle_rate_factor;
663 break;
665 case Priority.Generator:
666 case Priority.Delayed:
667 rate_factor = idle_scale * default_delayed_rate_factor;
668 break;
670 case Priority.Idle:
671 rate_factor = idle_scale * default_idle_rate_factor;
672 break;
675 // FIXME: we should do something more sophisticated than this
676 // with the load average.
677 // Random numbers galore!
678 double load_average = SystemInformation.LoadAverageOneMinute;
679 if (load_average > 3.001)
680 rate_factor *= 5.002;
681 else if (load_average > 1.5003)
682 rate_factor *= 2.004;
684 double delay = rate_factor * duration_of_previous_task;
686 // space out delayed tasks a bit when we aren't idle
687 if (! is_idle
688 && priority_of_next_task == Priority.Delayed
689 && delay < 0.5)
690 delay = 0.5;
692 if (delay > default_maximum_delay)
693 delay = default_maximum_delay;
695 // If we need to throttle, make sure we don't delay less than
696 // a second and some.
697 if (need_throttle && delay < 1.25)
698 delay = 1.25;
700 return delay;
704 // The main loop
707 // A convenience function. There should be a
708 // constructor to TimeSpan that does this.
709 private static TimeSpan TimeSpanFromSeconds (double t)
711 // Wait barfs if you hand it a negative TimeSpan,
712 // so we are paranoid;
713 if (t < 0.001)
714 t = 0;
716 // 1 tick = 100 nanoseconds
717 long ticks = (long) (t * 1.0e+7);
718 return new TimeSpan (ticks);
721 #if false
722 private void DescribeTaskQueue (string note, int i0, int i1)
724 Console.WriteLine ("----------------------");
725 Console.WriteLine (note);
726 for (int i=i0; i<i1; ++i) {
727 Task t = task_queue [i] as Task;
728 string xxx;
729 if (t == null)
730 xxx = "(null)";
731 else if (t.Cancelled)
732 xxx = t.Tag + " CANCELLED";
733 else
734 xxx = t.Tag;
735 Console.WriteLine ("{0}: {1}", i, xxx);
737 Console.WriteLine ("----------------------");
739 #endif
741 // Remove nulls and cancelled tasks from the queue.
742 // Note: this does no locking!
743 private void CleanQueue ()
745 int i = task_queue.Count - 1;
746 while (i >= 0) {
747 Task t = task_queue [i] as Task;
748 if (t != null) {
749 if (! t.Cancelled)
750 break;
751 // Remove cancelled items from the tag hash
752 task_by_tag.Remove (t.Tag);
754 --i;
756 if (i < task_queue.Count - 1)
757 task_queue.RemoveRange (i+1, task_queue.Count - 1 - i);
760 private void Worker ()
762 DateTime time_of_last_task = DateTime.MinValue;
763 double duration_of_last_task = 1;
765 Hook pre_hook = null;
766 Hook post_hook = null;
767 ArrayList collection = new ArrayList ();
769 while (running) {
771 lock (task_queue) {
773 Task task = null;
774 int task_i = -1;
775 int i;
777 // First, remove any null or cancelled tasks
778 // we find in the task_queue.
779 CleanQueue ();
781 // If the task queue is now empty, wait on our lock
782 // and then re-start our while loop
783 if (task_queue.Count == 0) {
784 next_task_time = new DateTime ();
785 status_str = "Waiting on empty queue";
786 Monitor.Wait (task_queue);
787 status_str = "Working";
788 continue;
791 // Find the next event that is past it's trigger time.
792 i = task_queue.Count - 1;
793 DateTime now = DateTime.Now;
794 DateTime next_trigger_time = DateTime.MaxValue;
795 task = null;
796 while (i >= 0) {
797 Task t = task_queue [i] as Task;
798 if (t != null && ! t.Cancelled) {
799 if (t.TriggerTime < now) {
800 task = t;
801 task_i = i; // Remember the task's position in the queue.
802 break;
803 } else {
804 // Keep track of when the next possible trigger time is.
805 if (t.TriggerTime < next_trigger_time)
806 next_trigger_time = t.TriggerTime;
809 --i;
812 // If we didn't find a task, wait for the next trigger-time
813 // and then re-start our while loop.
814 if (task == null) {
815 next_task_time = next_trigger_time;
816 status_str = "Waiting for next trigger time.";
817 Monitor.Wait (task_queue, next_trigger_time - now);
818 next_task_time = new DateTime ();
819 status_str = "Working";
820 continue;
823 // If we did find a task, do we want to execute it right now?
824 // Or should we wait a bit?
826 // How should we space things out?
827 double delay = 0;
828 delay = ComputeDelay (task.Priority, duration_of_last_task);
829 delay = Math.Min (delay, (next_trigger_time - DateTime.Now).TotalSeconds);
831 // Adjust by the time that has actually elapsed since the
832 // last task.
833 delay -= (DateTime.Now - time_of_last_task).TotalSeconds;
835 // If we still need to wait a bit longer, wait for the appropriate
836 // amount of time and then re-start our while loop.
837 if (delay > 0.001) {
838 next_task_time = DateTime.Now.AddSeconds (delay);
839 status_str = "Waiting for next task.";
840 // Never wait more than 15 seconds.
841 Monitor.Wait (task_queue, TimeSpanFromSeconds (Math.Min (delay, 15)));
842 next_task_time = new DateTime ();
843 status_str = "Working";
844 continue;
847 // Remove this task from the queue
848 task_queue [task_i] = null;
849 task_by_tag.Remove (task.Tag);
851 if (task.Collector == null) {
853 pre_hook = null;
854 post_hook = null;
855 collection.Add (task);
857 } else {
859 // Collect stuff
861 pre_hook = new Hook (task.Collector.PreTaskHook);
862 post_hook = new Hook (task.Collector.PostTaskHook);
864 double weight = task.Weight;
865 double min_weight = task.Collector.GetMinimumWeight ();
866 double max_weight = task.Collector.GetMaximumWeight ();
868 collection.Add (task);
870 // We left i pointing at task
871 --i;
872 while (i >= 0 && weight < max_weight) {
873 Task t = task_queue [i] as Task;
874 if (t != null
875 && ! t.Cancelled
876 && t.Collector == task.Collector
877 && t.TriggerTime < now) {
879 // Only include differently-prioritized tasks
880 // in the same collection if the total weight so far
881 // is below the minimum.
882 if (t.Priority != task.Priority && weight > min_weight)
883 break;
885 weight += t.Weight;
886 if (weight > max_weight)
887 break;
889 collection.Add (t);
891 // Remove the task from the queue and clean
892 // up the by-tag hash table.
893 task_queue [i] = null;
894 task_by_tag.Remove (t.Tag);
896 --i;
900 // Clean the queue again
901 // (We need to do this to keep rescheduled tasks from blocking
902 // stuff from getting cleaned off the end of the queue)
903 CleanQueue ();
905 Monitor.Pulse (task_queue);
909 // If we actually found tasks we like, do them now.
910 if (collection.Count > 0) {
911 DateTime t1 = DateTime.Now;
912 if (pre_hook != null) {
913 try {
914 pre_hook ();
915 } catch (Exception ex) {
916 Logger.Log.Error ("Caught exception in pre_hook '{0}'", pre_hook);
917 Logger.Log.Error (ex);
920 foreach (Task task in collection) {
921 task.DoTask ();
922 ++executed_task_count;
924 if (post_hook != null) {
925 try {
926 post_hook ();
927 } catch (Exception ex) {
928 Logger.Log.Error ("Caught exception in post_hook '{0}'", post_hook);
929 Logger.Log.Error (ex);
932 DateTime t2 = DateTime.Now;
934 duration_of_last_task = (t2 - t1).TotalSeconds;
935 time_of_last_task = t2;
937 pre_hook = null;
938 post_hook = null;
939 collection.Clear ();
943 // Execute all shutdown tasks
944 while (shutdown_task_queue.Count > 0) {
945 Task t = shutdown_task_queue.Dequeue () as Task;
946 if (t != null && ! t.Cancelled && t.Priority == Priority.Shutdown) {
947 try {
948 // FIXME: Support Pre/Post task hooks
949 t.DoTask ();
950 } catch (Exception ex) {
951 Logger.Log.Error ("Caught exception while performing shutdown tasks in the scheduler");
952 Logger.Log.Error (ex);
957 if (Debug)
958 Logger.Log.Debug ("Scheduler.Worker finished");
962 #if false
963 class TestTask : Scheduler.Task {
965 private class TestCollector : Scheduler.ITaskCollector {
967 public double GetMinimumWeight ()
969 return 0;
972 public double GetMaximumWeight ()
974 return 5;
977 public void PreTaskHook ()
979 Console.WriteLine ("+++ Pre-Task Hook");
982 public void PostTaskHook ()
984 Console.WriteLine ("+++ Post-Task Hook");
988 protected override void DoTaskReal ()
990 Console.WriteLine ("Doing task '{0}' at {1}", Tag, DateTime.Now);
991 Thread.Sleep (200);
992 if (Tag == "Bar")
993 Reschedule = true;
996 static void BeginTaskGroup ()
998 Console.WriteLine ("--- Begin Task Group!");
1001 static void EndTaskGroup ()
1003 Console.WriteLine ("--- End Task Group!");
1006 static void Main ()
1008 Scheduler sched = Scheduler.Global;
1010 Scheduler.TaskGroup tg = Scheduler.NewTaskGroup ("foo",
1011 new Scheduler.Hook (BeginTaskGroup),
1012 new Scheduler.Hook (EndTaskGroup));
1014 sched.Start ();
1016 Scheduler.Task task;
1018 task = new TestTask ();
1019 task.Tag = "Foo";
1020 task.AddTaskGroup (tg);
1021 task.Priority = Scheduler.Priority.Delayed;
1022 task.TriggerTime = DateTime.Now.AddSeconds (7);
1023 sched.Add (task);
1025 task = new TestTask ();
1026 task.Tag = "Bar";
1027 task.AddTaskGroup (tg);
1028 task.Priority = Scheduler.Priority.Delayed;
1029 sched.Add (task);
1031 Scheduler.ITaskCollector collector = null;
1032 for (int i = 0; i < 20; ++i) {
1033 if ((i % 10) == 0)
1034 collector = new TestCollector ();
1035 task = new TestTask ();
1036 task.Tag = String.Format ("Baboon {0}", i);
1037 task.Collector = collector;
1038 task.Priority = Scheduler.Priority.Delayed;
1039 sched.Add (task);
1042 while (true) {
1043 Thread.Sleep (1000);
1047 #endif