New e-d-s backend which indexes all local addressbooks and calendars.
[beagle.git] / Util / Scheduler.cs
blob4ab93b46d7019da5391ce9960a5865aba7371a5c
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 private double global_delay = -1.0;
37 static private bool immediate_priority_only = false;
38 static private string exercise_the_dog = null;
40 static Scheduler ()
42 // We used to support the EXERCISE_THE_DOG env variable, but
43 // that was dropped.
44 exercise_the_dog = Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG");
45 if (exercise_the_dog == null &&
46 Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG_HARDER") != null)
47 exercise_the_dog = "harder fallback";
49 if (exercise_the_dog != null) {
51 global_delay = 0.0;
53 if (exercise_the_dog.Length > 2 && exercise_the_dog [0] == 't')
54 global_delay = Double.Parse (exercise_the_dog.Substring (1));
57 if (Environment.GetEnvironmentVariable ("BEAGLE_IMMEDIATE_PRIORITY_ONLY") != null)
58 immediate_priority_only = true;
61 //////////////////////////////////////////////////////////////////////////////
63 static private Scheduler global = new Scheduler ();
65 static public Scheduler Global {
66 get { return global; }
69 //////////////////////////////////////////////////////////////////////////////
71 public enum Priority {
72 Shutdown = 0, // Do it on shutdown
73 Idle = 1, // Do it when the system is idle
74 Generator = 2, // Do it soon, but not *too* soon
75 Delayed = 3, // Do it soon
76 Immediate = 4, // Do it right now
79 public delegate void Hook ();
80 public delegate void TaskHook (Task task);
82 //////////////////////////////////////////////////////////////////////////////
84 public abstract class Task : IComparable {
86 // A unique identifier
87 public string Tag;
89 // Some metadata
90 public string Creator;
91 public string Description;
93 public Priority Priority = Priority.Idle;
94 public int SubPriority = 0;
96 public DateTime Timestamp;
97 public DateTime TriggerTime = DateTime.MinValue;
99 public ITaskCollector Collector = null;
100 public double Weight = 1.0;
102 public bool Reschedule = false;
104 ///////////////////////////////
106 private ArrayList task_groups = null;
108 public void AddTaskGroup (TaskGroup group)
110 if (task_groups == null)
111 task_groups = new ArrayList ();
112 task_groups.Add (group);
115 private void IncrementAllTaskGroups ()
117 if (task_groups != null) {
118 foreach (TaskGroupPrivate group in task_groups) {
119 if (! group.Finished)
120 group.Increment ();
125 private void DecrementAllTaskGroups ()
127 if (task_groups != null) {
128 foreach (TaskGroupPrivate group in task_groups) {
129 if (! group.Finished)
130 group.Decrement ();
135 private void TouchAllTaskGroups ()
137 if (task_groups != null) {
138 foreach (TaskGroupPrivate group in task_groups) {
139 if (! group.Finished)
140 group.Touch ();
145 ///////////////////////////////
147 private Scheduler scheduler = null;
149 public Scheduler ThisScheduler {
150 get { return scheduler; }
153 public void Schedule (Scheduler scheduler)
155 // Increment the task groups the first
156 // time a task is scheduled.
157 if (this.scheduler == null)
158 IncrementAllTaskGroups ();
159 this.scheduler = scheduler;
162 ///////////////////////////////
164 private bool cancelled = false;
166 public bool Cancelled {
167 get { return cancelled; }
170 public void Cancel ()
172 if (! cancelled)
173 DecrementAllTaskGroups ();
174 cancelled = true;
177 ///////////////////////////////
179 // The Task's count keeps track of how many
180 // times it has been executed.
182 private int count = 0;
184 public int Count {
185 get { return count; }
188 ///////////////////////////////
190 public void DoTask ()
192 if (! cancelled) {
193 TouchAllTaskGroups ();
194 try {
195 Logger.Log.Debug ("Starting task {0}", Tag);
196 Stopwatch sw = new Stopwatch ();
197 sw.Start ();
198 DoTaskReal ();
199 sw.Stop ();
200 Logger.Log.Debug ("Finished task {0} in {1}", Tag, sw);
201 } catch (Exception ex) {
202 Logger.Log.Warn ("Caught exception in DoTaskReal");
203 Logger.Log.Warn (" Tag: {0}", Tag);
204 Logger.Log.Warn (" Creator: {0}", Creator);
205 Logger.Log.Warn ("Description: {0}", Description);
206 Logger.Log.Warn (" Priority: {0} ({1})", Priority, SubPriority);
207 Logger.Log.Warn (ex);
209 if (Reschedule) {
210 Reschedule = false;
211 ++count;
212 ThisScheduler.Add (this);
213 } else {
214 DecrementAllTaskGroups ();
219 protected abstract void DoTaskReal ();
221 ///////////////////////////////
223 // Sort from lowest to highest priority
224 public int CompareTo (object obj)
226 Task other = obj as Task;
227 if (other == null)
228 return 1;
230 int cmp;
231 cmp = this.Priority.CompareTo (other.Priority);
232 if (cmp != 0)
233 return cmp;
235 cmp = this.SubPriority.CompareTo (other.SubPriority);
236 if (cmp != 0)
237 return cmp;
239 cmp = other.Timestamp.CompareTo (this.Timestamp);
240 if (cmp != 0)
241 return cmp;
243 // Try to break any ties
244 return this.GetHashCode ().CompareTo (other.GetHashCode ());
247 public override string ToString ()
249 StringBuilder sb = new StringBuilder ();
251 sb.AppendFormat ("{0} {1}\n", Priority, SubPriority);
253 sb.Append (Tag + "\n");
255 double t = (TriggerTime - DateTime.Now).TotalSeconds;
256 if (t > 0) {
257 if (t < 120)
258 sb.AppendFormat ("Trigger in {0:0.00} seconds\n", t);
259 else
260 sb.AppendFormat ("Trigger at {0}\n", TriggerTime);
263 if (Creator != null)
264 sb.AppendFormat ("Creator: {0}\n", Creator);
266 if (Description != null)
267 sb.Append (Description + "\n");
269 return sb.ToString ();
273 private class TaskHookWrapper : Task {
275 TaskHook hook;
277 public TaskHookWrapper (TaskHook hook)
279 this.hook = hook;
282 protected override void DoTaskReal ()
284 if (hook != null)
285 hook (this);
289 public static Task TaskFromHook (TaskHook hook)
291 return new TaskHookWrapper (hook);
294 //////////////////////////////////////////////////////////////////////////////
297 // Task Groups
300 public static TaskGroup NewTaskGroup (string name, Hook pre_hook, Hook post_hook)
302 return new TaskGroupPrivate (name, pre_hook, post_hook);
305 // We split the task group data structure into two parts:
306 // TaskGroup and TaskGroupPrivate. The TaskGroup we hand
307 // back to the user exposes minimal functionality.
308 public abstract class TaskGroup {
309 private string name;
311 protected TaskGroup (string name) {
312 this.name = name;
315 public string Name {
316 get { return name; }
319 public abstract bool Finished { get; }
322 private class TaskGroupPrivate : TaskGroup {
323 private int task_count = 0;
324 private bool touched = false;
325 private bool finished = false;
326 private Hook pre_hook;
327 private Hook post_hook;
329 public TaskGroupPrivate (string name,
330 Hook pre_hook,
331 Hook post_hook) : base (name)
333 this.pre_hook = pre_hook;
334 this.post_hook = post_hook;
337 public override bool Finished {
338 get { return finished; }
341 public void Increment ()
343 if (finished)
344 throw new Exception ("Tried to increment a finished TaskGroup");
345 ++task_count;
348 public void Touch ()
350 if (finished)
351 throw new Exception ("Tried to touch a finished TaskGroup");
353 if (! touched) {
354 if (pre_hook != null) {
355 try {
356 pre_hook ();
357 } catch (Exception ex) {
358 Logger.Log.Warn ("Caught exception in pre_hook of task group '{0}'", Name);
359 Logger.Log.Warn (ex);
362 touched = true;
366 public void Decrement ()
368 if (finished)
369 throw new Exception ("Tried to decrement a finished TaskGroup");
371 --task_count;
372 // Only fire our post-hook if the pre-hook fired
373 // (or would have fired, had it been non-null)
374 if (task_count == 0 && touched) {
375 if (post_hook != null) {
376 try {
377 post_hook ();
378 } catch (Exception ex) {
379 Logger.Log.Warn ("Caught exception in post_hook of task group '{0}'", Name);
380 Logger.Log.Warn (ex);
383 finished = true;
388 //////////////////////////////////////////////////////////////////////////////
391 // Task Collector
393 // This is a mechanism for executing tasks in sets, possibly outside of
394 // priority order.
397 public interface ITaskCollector {
399 double GetMinimumWeight ();
400 double GetMaximumWeight ();
402 void PreTaskHook ();
403 void PostTaskHook ();
406 //////////////////////////////////////////////////////////////////////////////
408 // FIXME: shutdown tasks should probably be ordered by something
409 private Queue shutdown_task_queue = new Queue ();
411 private ArrayList task_queue = new ArrayList ();
412 private Hashtable task_by_tag = new Hashtable ();
413 private int executed_task_count = 0;
415 public enum AddType {
416 DeferToExisting,
417 OptionallyReplaceExisting,
418 OnlyReplaceExisting
422 public bool Add (Task task, AddType add_type)
424 Task old_task = null;
426 lock (task_queue) {
427 if (task != null) {
429 if (immediate_priority_only && task.Priority != Priority.Immediate)
430 return false;
432 // Keep track of when immediate priority tasks are
433 // added so that we can throttle if the scheduler
434 // is being slammed with them.
435 if (task.Priority == Priority.Immediate) {
436 // Shift our times down by one
437 Array.Copy (last_immediate_times, 1, last_immediate_times, 0, 4);
438 last_immediate_times [4] = DateTime.Now;
441 old_task = task_by_tag [task.Tag] as Task;
442 if (old_task == task)
443 return true;
445 if (add_type == AddType.DeferToExisting
446 && old_task != null)
447 return false;
449 if (add_type == AddType.OnlyReplaceExisting
450 && old_task == null)
451 return false;
453 #if false
454 Logger.Log.Debug ("Adding task");
455 Logger.Log.Debug ("Tag: {0}", task.Tag);
456 if (task.Description != null)
457 Logger.Log.Debug ("Desc: {0}", task.Description);
458 #endif
460 task.Timestamp = DateTime.Now;
461 task.Schedule (this);
463 if (task.Priority == Priority.Shutdown) {
464 shutdown_task_queue.Enqueue (task);
465 } else {
466 int i = task_queue.BinarySearch (task);
467 if (i < 0)
468 i = ~i;
469 task_queue.Insert (i, task);
470 task_by_tag [task.Tag] = task;
474 Monitor.Pulse (task_queue);
477 if (old_task != null)
478 old_task.Cancel ();
480 return true;
483 public bool Add (Task task)
485 return Add (task, AddType.OptionallyReplaceExisting);
488 public Task GetByTag (string tag)
490 lock (task_queue) {
491 return task_by_tag [tag] as Task;
495 public bool ContainsByTag (string tag)
497 Task task = GetByTag (tag);
498 return task != null && !task.Cancelled;
503 //////////////////////////////////////////////////////////////////////////////
505 private string status_str = null;
506 private DateTime next_task_time;
508 public string GetHumanReadableStatus ()
510 StringBuilder sb = new StringBuilder ();
512 sb.Append ("Scheduler:\n");
514 sb.Append (String.Format ("Count: {0}\n", executed_task_count));
516 if (next_task_time.Ticks > 0)
517 sb.Append (String.Format ("Next task in {0:0.00} seconds\n",
518 (next_task_time - DateTime.Now).TotalSeconds));
520 if (status_str != null)
521 sb.Append ("Status: " + status_str + "\n");
523 lock (task_queue) {
524 int pos = 1;
525 for (int i = task_queue.Count - 1; i >= 0; --i) {
526 Task task = task_queue [i] as Task;
527 if (task == null || task.Cancelled)
528 continue;
530 sb.AppendFormat ("{0} ", pos);
531 sb.Append (task.ToString ());
532 sb.Append ("\n");
534 ++pos;
537 if (pos == 1)
538 sb.Append ("Scheduler queue is empty.\n");
541 sb.Append ("\n");
543 return sb.ToString ();
546 //////////////////////////////////////////////////////////////////////////////
548 Thread thread = null;
549 public bool running = false;
551 public void Start ()
553 lock (this) {
554 if (thread != null)
555 return;
556 running = true;
557 thread = new Thread (new ThreadStart (Worker));
558 thread.Start ();
562 public void Stop ()
564 lock (this) {
565 if (running) {
566 running = false;
567 lock (task_queue)
568 Monitor.Pulse (task_queue);
574 // Delay Computations
576 // This code controls how we space out tasks
579 // FIXME: random magic constants
580 const double idle_threshold = 5.314159 * 60; // probably should be longer
581 const double idle_ramp_up_time = 5.271828 * 60; // probably should be longer
582 const double default_delayed_rate_factor = 9.03; // work about 1/10th of the time
583 const double default_idle_rate_factor = 2.097; // work about 1/3rd of the time
584 const double default_maximum_delay = 20; // never wait for more than 20s
586 DateTime[] last_immediate_times = new DateTime [5];
588 private double GetIdleTime ()
590 return SystemInformation.InputIdleTime;
593 // The return value and duration_of_previous_task are both measured in seconds.
594 private double ComputeDelay (Priority priority_of_next_task,
595 double duration_of_previous_task)
597 if (global_delay >= 0.0)
598 return global_delay;
600 double rate_factor;
602 rate_factor = 2.0;
604 // Do everything faster the longer we are idle.
605 double idle_time = GetIdleTime ();
606 double idle_scale = 1.0;
607 bool is_idle = false;
608 bool need_throttle = false;
610 // Never speed up if we are using the battery.
611 if (idle_time > idle_threshold && ! SystemInformation.UsingBattery) {
612 is_idle = true;
613 double t = (idle_time - idle_threshold) / idle_ramp_up_time;
614 idle_scale = (1 - Math.Min (t, 1.0));
617 switch (priority_of_next_task) {
619 case Priority.Immediate:
620 rate_factor = 0;
622 if (last_immediate_times [0] != DateTime.MinValue) {
623 TimeSpan last_add_delta = DateTime.Now.Subtract (last_immediate_times [4]);
625 // If less than a second has gone by since the
626 // last immediate task was added, there is
627 // still a torrent of events coming in, and we
628 // may need to throttle.
629 if (last_add_delta.Seconds <= 1) {
630 TimeSpan between_add_delta = last_immediate_times [4].Subtract (last_immediate_times [0]);
632 // At least 5 immediate tasks have been
633 // added in the last second. We
634 // definitely need to throttle.
635 if (between_add_delta.Seconds <= 1) {
636 Logger.Log.Debug ("Thottling immediate priority tasks");
637 need_throttle = true;
638 rate_factor = idle_scale * default_idle_rate_factor;
643 break;
645 case Priority.Generator:
646 case Priority.Delayed:
647 rate_factor = idle_scale * default_delayed_rate_factor;
648 break;
650 case Priority.Idle:
651 rate_factor = idle_scale * default_idle_rate_factor;
652 break;
655 // FIXME: we should do something more sophisticated than this
656 // with the load average.
657 // Random numbers galore!
658 double load_average = SystemInformation.LoadAverageOneMinute;
659 if (load_average > 3.001)
660 rate_factor *= 5.002;
661 else if (load_average > 1.5003)
662 rate_factor *= 2.004;
664 double delay = rate_factor * duration_of_previous_task;
666 // space out delayed tasks a bit when we aren't idle
667 if (! is_idle
668 && priority_of_next_task == Priority.Delayed
669 && delay < 0.5)
670 delay = 0.5;
672 if (delay > default_maximum_delay)
673 delay = default_maximum_delay;
675 // If we need to throttle, make sure we don't delay less than
676 // a second and some.
677 if (need_throttle && delay < 1.25)
678 delay = 1.25;
680 return delay;
684 // The main loop
687 // A convenience function. There should be a
688 // constructor to TimeSpan that does this.
689 private static TimeSpan TimeSpanFromSeconds (double t)
691 // Wait barfs if you hand it a negative TimeSpan,
692 // so we are paranoid;
693 if (t < 0.001)
694 t = 0;
696 // 1 tick = 100 nanoseconds
697 long ticks = (long) (t * 1.0e+7);
698 return new TimeSpan (ticks);
701 #if false
702 private void DescribeTaskQueue (string note, int i0, int i1)
704 Console.WriteLine ("----------------------");
705 Console.WriteLine (note);
706 for (int i=i0; i<i1; ++i) {
707 Task t = task_queue [i] as Task;
708 string xxx;
709 if (t == null)
710 xxx = "(null)";
711 else if (t.Cancelled)
712 xxx = t.Tag + " CANCELLED";
713 else
714 xxx = t.Tag;
715 Console.WriteLine ("{0}: {1}", i, xxx);
717 Console.WriteLine ("----------------------");
719 #endif
721 // Remove nulls and cancelled tasks from the queue.
722 // Note: this does no locking!
723 private void CleanQueue ()
725 int i = task_queue.Count - 1;
726 while (i >= 0) {
727 Task t = task_queue [i] as Task;
728 if (t != null) {
729 if (! t.Cancelled)
730 break;
731 // Remove cancelled items from the tag hash
732 task_by_tag.Remove (t.Tag);
734 --i;
736 if (i < task_queue.Count - 1)
737 task_queue.RemoveRange (i+1, task_queue.Count - 1 - i);
740 private void Worker ()
742 DateTime time_of_last_task = DateTime.MinValue;
743 double duration_of_last_task = 1;
745 Hook pre_hook = null;
746 Hook post_hook = null;
747 ArrayList collection = new ArrayList ();
749 while (running) {
751 lock (task_queue) {
753 Task task = null;
754 int task_i = -1;
755 int i;
757 // First, remove any null or cancelled tasks
758 // we find in the task_queue.
759 CleanQueue ();
761 // If the task queue is now empty, wait on our lock
762 // and then re-start our while loop
763 if (task_queue.Count == 0) {
764 next_task_time = new DateTime ();
765 status_str = "Waiting on empty queue";
766 Monitor.Wait (task_queue);
767 status_str = "Working";
768 continue;
771 // Find the next event that is past it's trigger time.
772 i = task_queue.Count - 1;
773 DateTime now = DateTime.Now;
774 DateTime next_trigger_time = DateTime.MaxValue;
775 task = null;
776 while (i >= 0) {
777 Task t = task_queue [i] as Task;
778 if (t != null && ! t.Cancelled) {
779 if (t.TriggerTime < now) {
780 task = t;
781 task_i = i; // Remember the task's position in the queue.
782 break;
783 } else {
784 // Keep track of when the next possible trigger time is.
785 if (t.TriggerTime < next_trigger_time)
786 next_trigger_time = t.TriggerTime;
789 --i;
792 // If we didn't find a task, wait for the next trigger-time
793 // and then re-start our while loop.
794 if (task == null) {
795 next_task_time = next_trigger_time;
796 status_str = "Waiting for next trigger time.";
797 Monitor.Wait (task_queue, next_trigger_time - now);
798 next_task_time = new DateTime ();
799 status_str = "Working";
800 continue;
803 // If we did find a task, do we want to execute it right now?
804 // Or should we wait a bit?
806 // How should we space things out?
807 double delay = 0;
808 delay = ComputeDelay (task.Priority, duration_of_last_task);
809 delay = Math.Min (delay, (next_trigger_time - DateTime.Now).TotalSeconds);
811 // Adjust by the time that has actually elapsed since the
812 // last task.
813 delay -= (DateTime.Now - time_of_last_task).TotalSeconds;
815 // If we still need to wait a bit longer, wait for the appropriate
816 // amount of time and then re-start our while loop.
817 if (delay > 0.001) {
818 next_task_time = DateTime.Now.AddSeconds (delay);
819 status_str = "Waiting for next task.";
820 // Never wait more than 15 seconds.
821 Monitor.Wait (task_queue, TimeSpanFromSeconds (Math.Min (delay, 15)));
822 next_task_time = new DateTime ();
823 status_str = "Working";
824 continue;
827 // Remove this task from the queue
828 task_queue [task_i] = null;
829 task_by_tag.Remove (task.Tag);
831 if (task.Collector == null) {
833 pre_hook = null;
834 post_hook = null;
835 collection.Add (task);
837 } else {
839 // Collect stuff
841 pre_hook = new Hook (task.Collector.PreTaskHook);
842 post_hook = new Hook (task.Collector.PostTaskHook);
844 double weight = task.Weight;
845 double min_weight = task.Collector.GetMinimumWeight ();
846 double max_weight = task.Collector.GetMaximumWeight ();
848 collection.Add (task);
850 // We left i pointing at task
851 --i;
852 while (i >= 0 && weight < max_weight) {
853 Task t = task_queue [i] as Task;
854 if (t != null
855 && ! t.Cancelled
856 && t.Collector == task.Collector
857 && t.TriggerTime < now) {
859 // Only include differently-prioritized tasks
860 // in the same collection if the total weight so far
861 // is below the minimum.
862 if (t.Priority != task.Priority && weight > min_weight)
863 break;
865 weight += t.Weight;
866 if (weight > max_weight)
867 break;
869 collection.Add (t);
871 // Remove the task from the queue and clean
872 // up the by-tag hash table.
873 task_queue [i] = null;
874 task_by_tag.Remove (t.Tag);
876 --i;
880 // Clean the queue again
881 // (We need to do this to keep rescheduled tasks from blocking
882 // stuff from getting cleaned off the end of the queue)
883 CleanQueue ();
887 // If we actually found tasks we like, do them now.
888 if (collection.Count > 0) {
889 DateTime t1 = DateTime.Now;
890 if (pre_hook != null) {
891 try {
892 pre_hook ();
893 } catch (Exception ex) {
894 Logger.Log.Error ("Caught exception in pre_hook '{0}'", pre_hook);
895 Logger.Log.Error (ex);
898 foreach (Task task in collection) {
899 task.DoTask ();
900 ++executed_task_count;
902 if (post_hook != null) {
903 try {
904 post_hook ();
905 } catch (Exception ex) {
906 Logger.Log.Error ("Caught exception in post_hook '{0}'", post_hook);
907 Logger.Log.Error (ex);
910 DateTime t2 = DateTime.Now;
912 duration_of_last_task = (t2 - t1).TotalSeconds;
913 time_of_last_task = t2;
915 pre_hook = null;
916 post_hook = null;
917 collection.Clear ();
921 // Execute all shutdown tasks
922 while (shutdown_task_queue.Count > 0) {
923 Task t = shutdown_task_queue.Dequeue () as Task;
924 if (t != null && ! t.Cancelled && t.Priority == Priority.Shutdown) {
925 try {
926 // FIXME: Support Pre/Post task hooks
927 t.DoTask ();
928 } catch (Exception ex) {
929 Logger.Log.Error ("Caught exception while performing shutdown tasks in the scheduler");
930 Logger.Log.Error (ex);
935 Logger.Log.Debug ("Scheduler.Worker finished");
939 #if false
940 class TestTask : Scheduler.Task {
942 private class TestCollector : Scheduler.ITaskCollector {
944 public double GetMinimumWeight ()
946 return 0;
949 public double GetMaximumWeight ()
951 return 5;
954 public void PreTaskHook ()
956 Console.WriteLine ("+++ Pre-Task Hook");
959 public void PostTaskHook ()
961 Console.WriteLine ("+++ Post-Task Hook");
965 protected override void DoTaskReal ()
967 Console.WriteLine ("Doing task '{0}' at {1}", Tag, DateTime.Now);
968 Thread.Sleep (200);
969 if (Tag == "Bar")
970 Reschedule = true;
973 static void BeginTaskGroup ()
975 Console.WriteLine ("--- Begin Task Group!");
978 static void EndTaskGroup ()
980 Console.WriteLine ("--- End Task Group!");
983 static void Main ()
985 Scheduler sched = Scheduler.Global;
987 Scheduler.TaskGroup tg = Scheduler.NewTaskGroup ("foo",
988 new Scheduler.Hook (BeginTaskGroup),
989 new Scheduler.Hook (EndTaskGroup));
991 sched.Start ();
993 Scheduler.Task task;
995 task = new TestTask ();
996 task.Tag = "Foo";
997 task.AddTaskGroup (tg);
998 task.Priority = Scheduler.Priority.Delayed;
999 task.TriggerTime = DateTime.Now.AddSeconds (7);
1000 sched.Add (task);
1002 task = new TestTask ();
1003 task.Tag = "Bar";
1004 task.AddTaskGroup (tg);
1005 task.Priority = Scheduler.Priority.Delayed;
1006 sched.Add (task);
1008 Scheduler.ITaskCollector collector = null;
1009 for (int i = 0; i < 20; ++i) {
1010 if ((i % 10) == 0)
1011 collector = new TestCollector ();
1012 task = new TestTask ();
1013 task.Tag = String.Format ("Baboon {0}", i);
1014 task.Collector = collector;
1015 task.Priority = Scheduler.Priority.Delayed;
1016 sched.Add (task);
1019 while (true) {
1020 Thread.Sleep (1000);
1024 #endif