4 // Copyright (C) 2004-2005 Novell, Inc.
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.
28 using System
.Collections
;
30 using System
.Threading
;
32 using System
.Xml
.Serialization
;
34 namespace Beagle
.Util
{
36 public class Scheduler
{
38 // Fire an event if there are no tasks left to execute.
39 public delegate void EmptyQueueDelegate ();
40 public event EmptyQueueDelegate EmptyQueueEvent
;
42 public static bool Debug
= false;
44 public enum Priority
{
46 Shutdown
= 0, // Do it on shutdown
48 Idle
= 1, // Execute only when the whole machine is idle
49 // Probably should be reserved for computationally-expensive stuff
50 // FIXME: These are not properly scheduled right now
52 Maintenance
= 2, // Only execute when there are no lower-priority
53 // tasks from the same source to execute instead
55 Delayed
= 3, // Do it soon
57 Immediate
= 4, // Do it right now
60 public delegate void Hook ();
61 public delegate void TaskHook (Task task
);
63 //////////////////////////////////////////////////////////////////////////////
65 public abstract class Task
: IComparable
{
67 private string tag
= null;
68 private Priority priority
= Priority
.Delayed
;
69 private int sub_priority
= 0;
70 private DateTime trigger_time
= DateTime
.MinValue
;
71 private DateTime timestamp
; // when added to the scheduler
74 public string Creator
;
75 public string Description
;
77 public object Source
= null; // this is just an opaque identifier
79 public ITaskCollector Collector
= null;
80 public double Weight
= 1.0;
82 public bool Reschedule
= false;
84 private ArrayList task_groups
= null;
85 private TaskGroupPrivate child_task_group
= null;
87 ///////////////////////////////
90 // The tag is the task's unique identifier
96 // Don't allow us to change the tag of a scheduled task
97 if (tag
== null || scheduler
== null)
100 throw new Exception ("Can't change tag of " + tag
+ "!");
104 public Priority Priority
{
106 get { return priority; }
109 if (priority
!= value) {
116 public int SubPriority
{
118 get { return sub_priority; }
121 if (sub_priority
!= value) {
122 sub_priority
= value;
128 public DateTime TriggerTime
{
130 get { return trigger_time; }
133 if (trigger_time
!= value) {
134 trigger_time
= value;
140 public DateTime Timestamp
{
141 get { return timestamp; }
144 ///////////////////////////////
146 public void AddTaskGroup (TaskGroup
group)
148 if (task_groups
== null)
149 task_groups
= new ArrayList ();
150 task_groups
.Add (group);
153 private void IncrementAllTaskGroups ()
155 if (task_groups
!= null) {
156 foreach (TaskGroupPrivate
group in task_groups
) {
157 if (! group.Finished
)
163 private void DecrementAllTaskGroups ()
165 if (task_groups
!= null) {
166 foreach (TaskGroupPrivate
group in task_groups
) {
167 if (! group.Finished
)
173 private void TouchAllTaskGroups ()
175 if (task_groups
!= null) {
176 foreach (TaskGroupPrivate
group in task_groups
) {
177 if (! group.Finished
)
183 ///////////////////////////////
185 private Scheduler scheduler
= null;
187 public void Schedule (Scheduler scheduler
)
189 // Increment the task groups the first
190 // time a task is scheduled.
191 if (this.scheduler
== null)
192 IncrementAllTaskGroups ();
193 this.timestamp
= DateTime
.Now
;
194 this.scheduler
= scheduler
;
195 this.cancelled
= false;
198 private void Recompute ()
200 if (scheduler
!= null)
201 scheduler
.Recompute ();
204 ///////////////////////////////
206 // A blocked task will not execute.
207 public bool Blocked
{
209 // Block the task if we have unexecuted children
210 return child_task_group
!= null && ! child_task_group
.Finished
;
214 ///////////////////////////////
216 private bool cancelled
= false;
218 public bool Cancelled
{
219 get { return cancelled; }
222 public void Cancel ()
225 DecrementAllTaskGroups ();
226 Cleanup (); // clean up after cancelled tasks
231 ///////////////////////////////
233 // The Task's count keeps track of how many
234 // times it has been executed.
236 private int count
= 0;
239 get { return count; }
242 ///////////////////////////////
244 public void SpawnChild (Task child_task
)
246 if (child_task_group
== null)
247 child_task_group
= new TaskGroupPrivate ("Children of " + Tag
, null, null);
248 child_task
.AddTaskGroup (child_task_group
);
249 child_task
.Source
= this.Source
;
250 scheduler
.Add (child_task
);
253 ///////////////////////////////
255 public void DoTask ()
259 Logger
.Log
.Debug ("Starting task {0}", Tag
);
260 child_task_group
= null;
262 TouchAllTaskGroups ();
264 Stopwatch sw
= new Stopwatch ();
269 } catch (Exception ex
) {
271 "Caught exception in DoTaskReal\n" +
274 "Description: {0}\n" +
275 " Priority: {0} ({1})",
276 Tag
, Creator
, Description
, Priority
, SubPriority
);
280 Logger
.Log
.Debug ("Finished task {0} in {1}", Tag
, sw
);
284 scheduler
.Add (this); // re-add ourselves
286 DecrementAllTaskGroups ();
292 protected abstract void DoTaskReal ();
294 ///////////////////////////////
296 // Clean-up is called whenever we know that a task will never
297 // be executed. It is never called on tasks for who DoTaskReal
298 // has been called (except when rescheduled). Cleanup is also
299 // called when a task is cancelled.
301 public void Cleanup ()
305 } catch (Exception ex
) {
306 Logger
.Log
.Warn (ex
, "Caught exception cleaning up task '{0}'", Tag
);
310 protected virtual void DoCleanup ()
312 // Do nothing by default
315 ///////////////////////////////
317 // Sort from lowest to highest priority
318 // FIXME: This does not define a total ordering
319 // on the set of all tasks, so use it with care.
320 public int CompareTo (object obj
)
322 Task other
= obj
as Task
;
326 Priority this_priority
;
327 Priority other_priority
;
329 this_priority
= this.Priority
;
330 other_priority
= other
.Priority
;
332 // To other sources, Maintenance tasks looks like
334 if (this.Source
!= other
.Source
) {
335 if (this_priority
== Priority
.Maintenance
)
336 this_priority
= Priority
.Delayed
;
337 if (other_priority
== Priority
.Maintenance
)
338 other_priority
= Priority
.Delayed
;
342 cmp
= (int)this_priority
- (int)other_priority
;
346 cmp
= this.SubPriority
- other
.SubPriority
;
350 // Tasks that were added to the scheduler earlier take
351 // precedence over those that were added later.
352 cmp
= DateTime
.Compare (other
.Timestamp
, this.Timestamp
);
356 // Try to break any ties
357 return this.GetHashCode () - other
.GetHashCode ();
360 public void AppendToStringBuilder (StringBuilder sb
)
362 sb
.Append (Priority
).Append (' ').Append (SubPriority
);
363 sb
.Append (" (").Append (Timestamp
).Append (")\n");
365 sb
.Append (Tag
).Append ('\n');
367 double t
= (TriggerTime
- DateTime
.Now
).TotalSeconds
;
370 sb
.AppendFormat ("Hold for {0:0.00} seconds\n", t
);
372 sb
.Append ("Hold until ").Append (TriggerTime
);
378 sb
.Append ("Creator: ").Append (Creator
).Append ('\n');
380 if (Description
!= null)
381 sb
.Append (Description
).Append ('\n');
385 private class TaskHookWrapper
: Task
{
389 public TaskHookWrapper (TaskHook hook
)
394 protected override void DoTaskReal ()
401 public static Task
TaskFromHook (TaskHook hook
)
403 return new TaskHookWrapper (hook
);
406 //////////////////////////////////////////////////////////////////////////////
412 public static TaskGroup
NewTaskGroup (string name
, Hook pre_hook
, Hook post_hook
)
414 return new TaskGroupPrivate (name
, pre_hook
, post_hook
);
417 // The TaskGroup we hand back to the user is an interface that
418 // exposes minimal functionality.
419 public interface TaskGroup
{
421 bool Finished { get; }
424 private class TaskGroupPrivate
: TaskGroup
{
426 private int task_count
= 0;
427 private bool touched
= false;
428 private bool finished
= false;
429 private Hook pre_hook
;
430 private Hook post_hook
;
432 public TaskGroupPrivate (string name
,
437 this.pre_hook
= pre_hook
;
438 this.post_hook
= post_hook
;
445 public bool Finished
{
446 get { return finished; }
449 // Call this when a task is added to the task group.
450 public void Increment ()
453 throw new Exception ("Tried to increment a finished TaskGroup");
457 // Call this when we execute a task in the task group.
461 throw new Exception ("Tried to touch a finished TaskGroup");
464 if (pre_hook
!= null) {
467 } catch (Exception ex
) {
468 Logger
.Log
.Warn (ex
, "Caught exception in pre_hook of task group '{0}'", Name
);
475 // Call this after a task in the task group is complete.
476 public void Decrement ()
479 throw new Exception ("Tried to decrement a finished TaskGroup");
482 // Only fire our post-hook if the pre-hook fired
483 // (or would have fired, had it been non-null)
484 if (task_count
== 0 && touched
) {
485 if (post_hook
!= null) {
488 } catch (Exception ex
) {
489 Logger
.Log
.Warn (ex
, "Caught exception in post_hook of task group '{0}'", Name
);
497 //////////////////////////////////////////////////////////////////////////////
502 // This is a mechanism for executing tasks in sets, possibly outside of
506 public interface ITaskCollector
{
508 double GetMaximumWeight ();
511 void PostTaskHook ();
514 //////////////////////////////////////////////////////////////////////////////
516 private static double global_delay
= -1.0;
521 exercise
= Environment
.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG");
523 if (exercise
!= null) {
524 if (exercise
.Length
> 2 && exercise
[0] == 't')
525 global_delay
= Double
.Parse (exercise
.Substring (1));
531 //////////////////////////////////////////////////////////////////////////////
533 private static Scheduler
global = new Scheduler ();
535 public static Scheduler Global
{
536 get { return global; }
539 //////////////////////////////////////////////////////////////////////////////
541 private object big_lock
= new object ();
543 // FIXME: shutdown tasks should probably be ordered by something
544 private Queue shutdown_task_queue
= new Queue ();
546 private Hashtable tasks_by_tag
= new Hashtable ();
547 private int total_executed_task_count
= 0;
549 public void Add (Task task
)
554 if (task
.Source
== null)
555 throw new Exception ("Attempting to add Task with no source!");
557 Task old_task
= null;
561 // Keep track of when immediate priority tasks are
562 // added so that we can throttle if the scheduler
563 // is being slammed with them.
564 if (task
.Priority
== Priority
.Immediate
) {
565 // Shift our times down by one
566 Array
.Copy (last_immediate_times
, 1, last_immediate_times
, 0, 4);
567 last_immediate_times
[4] = DateTime
.Now
;
570 old_task
= tasks_by_tag
[task
.Tag
] as Task
;
572 task
.Schedule (this);
574 // Re-adding the same task is basically a no-op --- we
575 // just update the timestamp and return.
576 if (old_task
== task
)
580 Logger
.Log
.Debug ("Adding task");
581 Logger
.Log
.Debug ("Tag: {0}", task
.Tag
);
582 if (task
.Description
!= null)
583 Logger
.Log
.Debug ("Desc: {0}", task
.Description
);
586 if (task
.Priority
== Priority
.Shutdown
)
587 shutdown_task_queue
.Enqueue (task
);
589 tasks_by_tag
[task
.Tag
] = task
;
591 Monitor
.Pulse (big_lock
);
594 // If we clobbered another task, call cancel on it.
595 // This happens after we release the lock, since
596 // cancellation could result in a task group post-hook
598 if (old_task
!= null)
602 public Task
GetByTag (string tag
)
605 return tasks_by_tag
[tag
] as Task
;
608 public bool ContainsByTag (string tag
)
610 Task task
= GetByTag (tag
);
611 return task
!= null && !task
.Cancelled
;
614 public void Recompute ()
617 Monitor
.Pulse (big_lock
);
620 //////////////////////////////////////////////////////////////////////////////
622 private Thread thread
= null;
623 public bool running
= false;
624 private static bool shutdown_requested
= false;
629 if (shutdown_requested
|| thread
!= null)
632 thread
= ExceptionHandlingThread
.Start (new ThreadStart (Worker
));
636 public void Stop (bool to_shutdown
)
639 shutdown_requested
= to_shutdown
;
644 status_str
= "Stopped";
645 Monitor
.Pulse (big_lock
);
656 // Delay Computations
658 // This code controls how we space out tasks
661 // FIXME: random magic constants
662 const double idle_threshold
= 5.314159 * 60; // probably should be longer
663 const double idle_ramp_up_time
= 5.271828 * 60; // probably should be longer
664 const double default_delayed_rate_factor
= 9.03; // work about 1/10th of the time
665 const double default_idle_rate_factor
= 2.097; // work about 1/3rd of the time
666 const double maximum_delay
= 20; // never wait for more than 20s
667 const double min_throttled_delay
= 1.5; // never wait less than this when throttled
668 const double min_overloaded_delay
= 2.2; // never wait less than this when there are many tasks
669 const int task_overload_threshold
= 60; // number of tasks to process before delaying
671 DateTime
[] last_immediate_times
= new DateTime
[5];
673 // The return value and duration_of_previous_task are both measured in seconds.
674 private double ComputeDelay (Priority priority_of_next_task
,
675 double duration_of_previous_task
,
676 int executed_task_count
)
678 if (global_delay
>= 0.0)
685 // Do everything faster the longer we are idle.
686 double idle_time
= SystemInformation
.InputIdleTime
;
687 double idle_scale
= 1.0;
688 bool is_idle
= false;
689 bool need_throttle
= false;
691 // Never speed up if we are using the battery.
692 if (idle_time
> idle_threshold
&& ! SystemInformation
.UsingBattery
) {
694 double t
= (idle_time
- idle_threshold
) / idle_ramp_up_time
;
695 idle_scale
= (1 - Math
.Min (t
, 1.0));
698 switch (priority_of_next_task
) {
700 case Priority
.Immediate
:
703 if (last_immediate_times
[0] != DateTime
.MinValue
) {
704 TimeSpan last_add_delta
= DateTime
.Now
.Subtract (last_immediate_times
[4]);
706 // If less than a second has gone by since the
707 // last immediate task was added, there is
708 // still a torrent of events coming in, and we
709 // may need to throttle.
710 if (last_add_delta
.TotalSeconds
<= 1) {
711 TimeSpan between_add_delta
= last_immediate_times
[4].Subtract (last_immediate_times
[0]);
713 // At least 5 immediate tasks have been
714 // added in the last second. We
715 // definitely need to throttle.
716 if (between_add_delta
.TotalSeconds
<= 1) {
717 need_throttle
= true;
718 rate_factor
= idle_scale
* default_idle_rate_factor
;
723 // If we've processed many tasks since the last
724 // time we took a break, ignore the priority and set a
725 // delay equivalent to Priority.Delayed.
726 if (!is_idle
&& executed_task_count
>= task_overload_threshold
)
727 rate_factor
= idle_scale
* default_delayed_rate_factor
;
731 case Priority
.Delayed
:
732 rate_factor
= idle_scale
* default_delayed_rate_factor
;
736 rate_factor
= idle_scale
* default_idle_rate_factor
;
741 // FIXME: we should do something more sophisticated than this
742 // with the load average.
743 // Random numbers galore!
744 double load_average
= SystemInformation
.LoadAverageOneMinute
;
745 if (load_average
> 3.001)
746 rate_factor
*= 5.002;
747 else if (load_average
> 1.5003)
748 rate_factor
*= 2.004;
750 double delay
= rate_factor
* duration_of_previous_task
;
752 // space out delayed tasks a bit when we aren't idle
754 && priority_of_next_task
== Priority
.Delayed
758 if (delay
> maximum_delay
)
759 delay
= maximum_delay
;
761 // If we need to throttle, make sure we don't delay less than
762 // a second and some.
763 if (need_throttle
&& delay
< min_throttled_delay
)
764 delay
= min_throttled_delay
;
766 // If we're not idle and we've just processed more
767 // than a certain number of events, take a break.
769 && executed_task_count
>= task_overload_threshold
770 && delay
< min_overloaded_delay
)
771 delay
= min_overloaded_delay
;
780 // A convenience function. There should be a
781 // constructor to TimeSpan that does this.
782 private static TimeSpan
TimeSpanFromSeconds (double t
)
784 // Wait barfs if you hand it a negative TimeSpan,
785 // so we are paranoid;
789 // 1 tick = 100 nanoseconds
790 long ticks
= (long) (t
* 1.0e+7);
791 return new TimeSpan (ticks
);
794 private string status_str
= null;
796 private void Worker ()
798 DateTime end_time_of_previous_task
= DateTime
.MinValue
;
799 double duration_of_previous_task
= 0.0;
801 Hook pre_hook
= null;
802 Hook post_hook
= null;
803 ArrayList to_be_executed
= new ArrayList ();
804 Hashtable max_priority_by_source
= new Hashtable ();
805 int executed_task_count
= 0;
806 StringBuilder status_builder
= new StringBuilder ();
810 status_str
= "Finding next task to execute";
814 // If there are no pending tasks, wait
815 // on our lock and then re-start our
817 if (tasks_by_tag
.Count
== 0) {
818 if (EmptyQueueEvent
!= null)
820 status_str
= "Waiting on empty queue";
821 Monitor
.Wait (big_lock
);
822 executed_task_count
= 0;
826 // Walk across our list of tasks and find
827 // the next one to execute.
828 DateTime now
= DateTime
.Now
;
829 DateTime next_trigger_time
= DateTime
.MaxValue
;
831 // Make a first pass over our tasks, finding the
832 // highest-priority item per source.
833 max_priority_by_source
.Clear ();
834 foreach (Task task
in tasks_by_tag
.Values
) {
835 if (task
.Blocked
|| task
.TriggerTime
>= now
)
837 if (max_priority_by_source
.Contains (task
.Source
)) {
838 Priority p
= (Priority
) max_priority_by_source
[task
.Source
];
839 if (p
< task
.Priority
)
840 max_priority_by_source
[task
.Source
] = task
.Priority
;
842 max_priority_by_source
[task
.Source
] = task
.Priority
;
846 // Now make a second pass over the tasks and find
847 // the highest-priority item. We use the information
848 // from the first pass to correctly prioritize maintenance tasks.
849 Task next_task
= null;
850 foreach (Task task
in tasks_by_tag
.Values
) {
853 if (task
.TriggerTime
>= now
) {
854 if (task
.TriggerTime
< next_trigger_time
)
855 next_trigger_time
= task
.TriggerTime
;
859 // If this is a maintenance task and there is a high-priority
860 // task from the same source, skip it.
861 if (task
.Priority
== Priority
.Maintenance
) {
862 Priority p
= (Priority
) max_priority_by_source
[task
.Source
];
863 if (p
> task
.Priority
)
867 if (task
.TriggerTime
< now
) {
868 if (next_task
== null || next_task
.CompareTo (task
) < 0)
873 // If we didn't find a task, wait for the next trigger-time
874 // and then re-start our while loop.
875 if (next_task
== null) {
876 if (next_trigger_time
== DateTime
.MaxValue
) {
877 status_str
= "Waiting for an unblocked task";
878 Monitor
.Wait (big_lock
);
880 status_str
= "Waiting for the next trigger time";
881 Monitor
.Wait (big_lock
, next_trigger_time
- now
);
883 executed_task_count
= 0;
887 // If we did find a task, do we want to execute it right now?
888 // Or should we wait a bit?
890 // How should we space things out?
892 delay
= ComputeDelay (next_task
.Priority
, duration_of_previous_task
, executed_task_count
);
893 delay
= Math
.Min (delay
, (next_trigger_time
- now
).TotalSeconds
);
895 // Adjust by the time that has actually elapsed since the
897 delay
-= (now
- end_time_of_previous_task
).TotalSeconds
;
899 // If we still need to wait a bit longer, wait for the appropriate
900 // amount of time and then re-start our while loop.
902 status_str
= "Waiting for next task.";
903 Monitor
.Wait (big_lock
, TimeSpanFromSeconds (delay
));
904 executed_task_count
= 0;
909 // If we've made it to this point, it is time to start
910 // executing our selected task.
913 to_be_executed
.Clear ();
915 if (next_task
.Collector
== null) {
917 to_be_executed
.Add (next_task
);
921 pre_hook
= new Hook (next_task
.Collector
.PreTaskHook
);
922 post_hook
= new Hook (next_task
.Collector
.PostTaskHook
);
924 // Find all eligible tasks with the same collector,
925 // and add them to the collection list.
927 foreach (Task task
in tasks_by_tag
.Values
)
928 if (task
!= next_task
929 && task
.Collector
== next_task
.Collector
931 && task
.TriggerTime
< now
)
932 to_be_executed
.Add (task
);
934 // Order the tasks from highest to lowest priority.
935 // Our original task will always be the first item
936 // in the resulting array.
937 to_be_executed
.Sort ();
938 to_be_executed
.Add (next_task
);
939 to_be_executed
.Reverse ();
941 // Now find how many tasks can be executed before we
942 // exceed the collector's maximum weight. If necessary,
943 // prune the list of tasks.
944 double remaining_weight
;
945 remaining_weight
= next_task
.Collector
.GetMaximumWeight ();
947 while (i
< to_be_executed
.Count
&& remaining_weight
> 0) {
949 task
= to_be_executed
[i
] as Task
;
950 remaining_weight
-= task
.Weight
;
953 if (i
< to_be_executed
.Count
)
954 to_be_executed
.RemoveRange (i
, to_be_executed
.Count
- i
);
957 // Remove the tasks we are about to execute from our
959 foreach (Task task
in to_be_executed
)
960 tasks_by_tag
.Remove (task
.Tag
);
962 // Pulse our lock, in case anyone is waiting for it.
963 Monitor
.Pulse (big_lock
);
966 // Now actually execute the set of tasks we found.
968 status_builder
.Length
= 0;
969 status_builder
.Append ("Executing task");
970 if (to_be_executed
.Count
> 1)
971 status_builder
.Append ('s');
972 status_builder
.Append ('\n');
973 foreach (Task task
in to_be_executed
) {
974 task
.AppendToStringBuilder (status_builder
);
975 status_builder
.Append ('\n');
977 status_str
= status_builder
.ToString ();
979 DateTime start_time
= DateTime
.Now
;
980 if (pre_hook
!= null) {
983 } catch (Exception ex
) {
984 Logger
.Log
.Error (ex
, "Caught exception in pre_hook '{0}'", pre_hook
);
987 foreach (Task task
in to_be_executed
) {
989 ++total_executed_task_count
;
990 ++executed_task_count
;
992 if (post_hook
!= null) {
995 } catch (Exception ex
) {
996 Logger
.Log
.Error (ex
, "Caught exception in post_hook '{0}'", post_hook
);
1000 end_time_of_previous_task
= DateTime
.Now
;
1001 duration_of_previous_task
= (end_time_of_previous_task
- start_time
).TotalSeconds
;
1004 // Execute all shutdown tasks
1005 foreach (Task task
in shutdown_task_queue
)
1006 if (! task
.Cancelled
&& ! task
.Blocked
)
1009 // Call Cleanup on all of our unexecuted tasks
1010 foreach (Task task
in tasks_by_tag
.Values
)
1014 Logger
.Log
.Debug ("Scheduler.Worker finished");
1017 //////////////////////////////////////////////////////////////////////////////
1019 private static StringBuilder cached_sb
= new StringBuilder ();
1021 public SchedulerInformation
GetCurrentStatus ()
1023 SchedulerInformation current_status
= new SchedulerInformation ();
1027 ArrayList blocked_tasks
= new ArrayList ();
1028 ArrayList future_tasks
= new ArrayList ();
1029 ArrayList pending_tasks
= new ArrayList ();
1031 DateTime now
= DateTime
.Now
;
1032 foreach (Task task
in tasks_by_tag
.Values
) {
1034 blocked_tasks
.Add (task
);
1035 else if (task
.TriggerTime
> now
)
1036 future_tasks
.Add (task
);
1038 pending_tasks
.Add (task
);
1041 blocked_tasks
.Sort ();
1042 blocked_tasks
.Reverse ();
1044 future_tasks
.Sort ();
1045 future_tasks
.Reverse ();
1047 pending_tasks
.Sort ();
1048 pending_tasks
.Reverse ();
1050 foreach (Task task
in pending_tasks
) {
1051 cached_sb
.Length
= 0;
1052 task
.AppendToStringBuilder (cached_sb
);
1053 current_status
.PendingTasks
.Add (cached_sb
.ToString ());
1056 foreach (Task task
in future_tasks
) {
1057 cached_sb
.Length
= 0;
1058 task
.AppendToStringBuilder (cached_sb
);
1059 current_status
.FutureTasks
.Add (cached_sb
.ToString ());
1062 foreach (Task task
in blocked_tasks
) {
1063 cached_sb
.Length
= 0;
1064 task
.AppendToStringBuilder (cached_sb
);
1065 current_status
.BlockedTasks
.Add (cached_sb
.ToString ());
1068 current_status
.TotalTaskCount
= total_executed_task_count
;
1069 current_status
.StatusString
= status_str
;
1073 return current_status
;
1078 public class SchedulerInformation
{
1080 public int TotalTaskCount
= -1;
1083 public string StatusString
;
1086 [XmlArrayItem (ElementName
="PendingTask", Type
=typeof (string))]
1087 public ArrayList PendingTasks
= new ArrayList ();
1090 [XmlArrayItem (ElementName
="FutureTask", Type
=typeof (string))]
1091 public ArrayList FutureTasks
= new ArrayList ();
1094 [XmlArrayItem (ElementName
="BlockedTask", Type
=typeof (string))]
1095 public ArrayList BlockedTasks
= new ArrayList ();
1097 private static StringBuilder sb
= new StringBuilder ();
1099 public string ToHumanReadableString ()
1103 sb
.Append ("Scheduler:\n");
1104 sb
.Append ("Count: ").Append (TotalTaskCount
);
1107 if (StatusString
!= null)
1108 sb
.Append ("Status: ").Append (StatusString
).Append ('\n');
1111 sb
.Append ("\nPending Tasks:\n");
1112 if (PendingTasks
!= null && PendingTasks
.Count
> 0) {
1113 foreach (string task
in PendingTasks
) {
1114 sb
.Append (pos
).Append (' ').Append (task
).Append ('\n');
1118 sb
.Append ("Scheduler queue is empty.\n");
1121 if (FutureTasks
!= null && FutureTasks
.Count
> 0) {
1122 sb
.Append ("\nFuture Tasks:\n");
1123 foreach (string task
in FutureTasks
)
1124 sb
.Append (task
).Append ('\n');
1127 if (BlockedTasks
!= null && BlockedTasks
.Count
> 0) {
1128 sb
.Append ("\nBlocked Tasks:\n");
1129 foreach (string task
in BlockedTasks
)
1130 sb
.Append (task
).Append ('\n');
1133 return sb
.ToString ();
1138 class TestTask
: Scheduler
.Task
{
1140 private class TestCollector
: Scheduler
.ITaskCollector
{
1142 public double GetMinimumWeight ()
1147 public double GetMaximumWeight ()
1152 public void PreTaskHook ()
1154 Console
.WriteLine ("+++ Pre-Task Hook");
1157 public void PostTaskHook ()
1159 Console
.WriteLine ("+++ Post-Task Hook");
1163 protected override void DoTaskReal ()
1165 Console
.WriteLine ("Doing task '{0}' at {1}", Tag
, DateTime
.Now
);
1171 private static void BeginTaskGroup ()
1173 Console
.WriteLine ("--- Begin Task Group!");
1176 private static void EndTaskGroup ()
1178 Console
.WriteLine ("--- End Task Group!");
1181 public static void Main ()
1183 Scheduler sched
= Scheduler
.Global
;
1185 Scheduler
.TaskGroup tg
= Scheduler
.NewTaskGroup ("foo",
1186 new Scheduler
.Hook (BeginTaskGroup
),
1187 new Scheduler
.Hook (EndTaskGroup
));
1191 Scheduler
.Task task
;
1193 task
= new TestTask ();
1195 task
.AddTaskGroup (tg
);
1196 task
.Priority
= Scheduler
.Priority
.Delayed
;
1197 task
.TriggerTime
= DateTime
.Now
.AddSeconds (7);
1200 task
= new TestTask ();
1202 task
.AddTaskGroup (tg
);
1203 task
.Priority
= Scheduler
.Priority
.Delayed
;
1206 Scheduler
.ITaskCollector collector
= null;
1207 for (int i
= 0; i
< 20; ++i
) {
1209 collector
= new TestCollector ();
1210 task
= new TestTask ();
1211 task
.Tag
= String
.Format ("Baboon {0}", i
);
1212 task
.Collector
= collector
;
1213 task
.Priority
= Scheduler
.Priority
.Delayed
;
1218 Thread
.Sleep (1000);