/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Threading; namespace Apache.NMS.ActiveMQ.Threads { /// /// A facility for applications to schedule tasks for future execution in a background /// thread. Tasks may be scheduled for one-time execution, or for repeated execution at /// regular intervals. Unlike the normal .NET Timer this Timer allows for multiple tasks /// to be scheduled in a single Timer object. /// /// Corresponding to each Timer object is a single background thread that is used to execute /// all of the timer's tasks, sequentially. Timer tasks should complete quickly. If a timer /// task takes excessive time to complete, it "hogs" the timer's task execution thread. This /// can, in turn, delay the execution of subsequent tasks, which may "bunch up" and execute /// in rapid succession when (and if) the offending task finally completes. /// /// After the last live reference to a Timer object goes away and all outstanding tasks have /// completed execution, the timer's task execution thread terminates gracefully (and becomes /// subject to garbage collection). However, this can take arbitrarily long to occur. By default, /// the task execution thread does not run as a Background thread, so it is capable of keeping an /// application from terminating. If a caller wants to terminate a timer's task execution thread /// rapidly, the caller should invoke the timer's cancel method. /// /// If the timer's task execution thread terminates unexpectedly, any further attempt to schedule /// a task on the timer will result in an IllegalStateException, as if the timer's cancel method /// had been invoked. /// /// This class is thread-safe: multiple threads can share a single Timer object without the /// need for external synchronization. /// /// This class does not offer real-time guarantees: it schedules tasks using the /// EventWaitHandle.WaitOne(TimeSpan) method. /// public class TimerEx { #region Static Id For Anonymous Timer Naming. private static long timerId; private static long NextId() { return Interlocked.Increment(ref timerId); } #endregion private readonly TimerImpl impl; // Used to finalize thread private readonly DisposeHelper disposal; public TimerEx(String name, bool isBackground) { if (name == null) { throw new NullReferenceException("name is null"); } this.impl = new TimerImpl(name, isBackground); this.disposal = new DisposeHelper(impl); } public TimerEx(String name) : this(name, false) { } public TimerEx(bool isBackground) : this("Timer-" + TimerEx.NextId().ToString(), isBackground) { } public TimerEx() : this(false) { } /// /// Terminates this timer, discarding any currently scheduled tasks. Does not interfere /// with a currently executing task (if it exists). Once a timer has been terminated, /// its execution thread terminates gracefully, and no more tasks may be scheduled on it. /// /// Note that calling this method from within the run method of a timer task that was /// invoked by this timer absolutely guarantees that the ongoing task execution is the /// last task execution that will ever be performed by this timer. /// /// This method may be called repeatedly; the second and subsequent calls have no effect. /// public void Cancel() { this.impl.Cancel(); } /// /// Removes all cancelled tasks from this timer's task queue. Calling this method has /// no effect on the behavior of the timer, but eliminates the references to the cancelled /// tasks from the queue. If there are no external references to these tasks, they become /// eligible for garbage collection. /// /// Most programs will have no need to call this method. It is designed for use by the /// rare application that cancels a large number of tasks. Calling this method trades /// time for space: the runtime of the method may be proportional to n + c log n, where /// n is the number of tasks in the queue and c is the number of cancelled tasks. /// /// Note that it is permissible to call this method from within a a task scheduled /// on this timer. /// public int Purge() { lock(this.impl.SyncRoot) { return impl.Purge(); } } public override string ToString() { return string.Format("[TimerEx{0}]", this.impl.Name); } #region WaitCallback Scheduling Methods /// /// Schedules the specified WaitCallback task for execution at the specified time. If the /// time is in the past, the task is scheduled for immediate execution. The method returns /// a TimerTask instance that can be used to later cancel the scheduled task. /// public TimerTask Schedule(WaitCallback callback, object arg, DateTime when) { InternalTimerTask task = new InternalTimerTask(callback, arg); TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false); return task; } /// /// Schedules the specified WaitCallback task for execution after the specified delay. /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask Schedule(WaitCallback callback, object arg, int delay) { if(delay < 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(-1), false); return task; } /// /// Schedules the specified WaitCallback task for execution after the specified delay. /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask Schedule(WaitCallback callback, object arg, TimeSpan delay) { if(delay.CompareTo(TimeSpan.Zero) < 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-delay execution, /// beginning after the specified delay. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask Schedule(WaitCallback callback, object arg, int delay, int period) { if(delay < 0 || period <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(period), false); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-delay execution, /// beginning after the specified delay. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask Schedule(WaitCallback callback, object arg, TimeSpan delay, TimeSpan period) { if(delay.CompareTo(TimeSpan.Zero) < 0 || period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); DoScheduleImpl(task, delay, period, false); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-delay execution, /// beginning at the specified start time. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask Schedule(WaitCallback callback, object arg, DateTime when, int period) { if (period <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(period), false); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-delay execution, /// beginning at the specified start time. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask Schedule(WaitCallback callback, object arg, DateTime when, TimeSpan period) { if (period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, period, false); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-rate execution, beginning /// after the specified delay. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask ScheduleAtFixedRate(WaitCallback callback, object arg, int delay, int period) { if(delay < 0 || period <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(period), true); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-rate execution, beginning /// after the specified delay. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask ScheduleAtFixedRate(WaitCallback callback, object arg, TimeSpan delay, TimeSpan period) { if(delay.CompareTo(TimeSpan.Zero) < 0 || period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); DoScheduleImpl(task, delay, period, true); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-rate execution, beginning /// at the specified time. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask ScheduleAtFixedRate(WaitCallback callback, object arg, DateTime when, int period) { if (period <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(period), true); return task; } /// /// Schedules the specified WaitCallback task for repeated fixed-rate execution, beginning /// at the specified time. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// /// The method returns a TimerTask instance that can be used to later cancel the /// scheduled task. /// public TimerTask ScheduleAtFixedRate(WaitCallback callback, object arg, DateTime when, TimeSpan period) { if (period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } InternalTimerTask task = new InternalTimerTask(callback, arg); TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, period, true); return task; } #endregion #region TimerTask Scheduling Methods /// /// Schedules the specified TimerTask for execution at the specified time. If the /// time is in the past. /// public void Schedule(TimerTask task, DateTime when) { TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false); } /// /// Schedules the specified TimerTask for execution after the specified delay. /// public void Schedule(TimerTask task, int delay) { if(delay < 0) { throw new ArgumentOutOfRangeException(); } DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(-1), false); } /// /// Schedules the specified TimerTask for execution after the specified delay. /// public void Schedule(TimerTask task, TimeSpan delay) { if(delay.CompareTo(TimeSpan.Zero) < 0) { throw new ArgumentOutOfRangeException(); } DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false); } /// /// Schedules the specified TimerTask for repeated fixed-delay execution, beginning /// after the specified delay. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// public void Schedule(TimerTask task, int delay, int period) { if(delay < 0 || period <= 0) { throw new ArgumentOutOfRangeException(); } DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(period), false); } /// /// Schedules the specified TimerTask for repeated fixed-delay execution, beginning /// after the specified delay. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// public void Schedule(TimerTask task, TimeSpan delay, TimeSpan period) { if(delay.CompareTo(TimeSpan.Zero) < 0 || period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } DoScheduleImpl(task, delay, period, false); } /// /// Schedules the specified TimerTask for repeated fixed-delay execution, beginning /// at the specified time. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// public void Schedule(TimerTask task, DateTime when, int period) { if (period <= 0) { throw new ArgumentOutOfRangeException(); } TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(period), false); } /// /// Schedules the specified TimerTask for repeated fixed-delay execution, beginning /// at the specified time. Subsequent executions take place at approximately /// regular intervals separated by the specified period. /// /// In fixed-delay execution, each execution is scheduled relative to the actual execution /// time of the previous execution. If an execution is delayed for any reason (such as /// garbage collection or other background activity), subsequent executions will be delayed. /// /// Fixed-delay execution is appropriate for recurring activities that require "smoothness." /// In other words, it is appropriate for activities where it is more important to keep the /// frequency accurate in the short run than in the long run. /// public void Schedule(TimerTask task, DateTime when, TimeSpan period) { if (period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, period, false); } /// /// Schedules the specified TimerTask for repeated fixed-rate execution, beginning /// after the specified delay. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// public void ScheduleAtFixedRate(TimerTask task, int delay, int period) { if(delay < 0 || period <= 0) { throw new ArgumentOutOfRangeException(); } DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(period), true); } /// /// Schedules the specified TimerTask for repeated fixed-rate execution, beginning /// after the specified delay. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// public void ScheduleAtFixedRate(TimerTask task, TimeSpan delay, TimeSpan period) { if(delay.CompareTo(TimeSpan.Zero) < 0 || period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } DoScheduleImpl(task, delay, period, true); } /// /// Schedules the specified TimerTask for repeated fixed-rate execution, beginning /// at the specified time. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// public void ScheduleAtFixedRate(TimerTask task, DateTime when, int period) { if (period <= 0) { throw new ArgumentOutOfRangeException(); } TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(period), true); } /// /// Schedules the specified TimerTask for repeated fixed-rate execution, beginning /// at the specified time. Subsequent executions take place at approximately regular /// intervals, separated by the specified period. /// /// In fixed-rate execution, each execution is scheduled relative to the scheduled execution /// time of the initial execution. If an execution is delayed for any reason (such as garbage /// collection or other background activity), two or more executions will occur in rapid /// succession to "catch up." /// /// Fixed-rate execution is appropriate for recurring activities that are sensitive to /// absolute time, such as ringing a chime every hour on the hour, or running scheduled /// maintenance every day at a particular time. /// public void ScheduleAtFixedRate(TimerTask task, DateTime when, TimeSpan period) { if (period.CompareTo(TimeSpan.Zero) <= 0) { throw new ArgumentOutOfRangeException(); } TimeSpan delay = when - DateTime.Now; DoScheduleImpl(task, delay, period, true); } #endregion #region Implementation of Scheduling method. private void DoScheduleImpl(TimerTask task, TimeSpan delay, TimeSpan period, bool fixedRate) { if (task == null) { throw new ArgumentNullException("TimerTask cannot be null"); } lock(this.impl.SyncRoot) { if (impl.Cancelled) { throw new InvalidOperationException(); } DateTime when = DateTime.Now + delay; lock(task.syncRoot) { if (task.IsScheduled) { throw new InvalidOperationException(); } if (task.cancelled) { throw new InvalidOperationException("Task is already cancelled"); } task.when = when; task.period = period; task.fixedRate = fixedRate; } // insert the newTask into queue impl.InsertTask(task); } } #endregion #region Interal TimerTask to invoking WaitCallback tasks private class InternalTimerTask : TimerTask { private WaitCallback task; private object taskArg; public InternalTimerTask(WaitCallback task, object taskArg) { if (task == null) { throw new ArgumentNullException("The WaitCallback task cannot be null"); } this.task = task; this.taskArg = taskArg; } public override void Run() { this.task(taskArg); } } #endregion #region Timer Heap that sorts Tasks into timed order. private sealed class TimerHeap { internal static readonly int DEFAULT_HEAP_SIZE = 256; internal TimerTask[] timers = new TimerTask[DEFAULT_HEAP_SIZE]; internal int size = 0; internal int deletedCancelledNumber = 0; public TimerTask Minimum() { return timers[0]; } public bool IsEmpty() { return size == 0; } public void Insert(TimerTask task) { if (timers.Length == size) { TimerTask[] appendedTimers = new TimerTask[size * 2]; timers.CopyTo(appendedTimers, 0); timers = appendedTimers; } timers[size++] = task; UpHeap(); } public void Delete(int pos) { // posible to delete any position of the heap if (pos >= 0 && pos < size) { timers[pos] = timers[--size]; timers[size] = null; DownHeap(pos); } } private void UpHeap() { int current = size - 1; int parent = (current - 1) / 2; while (timers[current].when < timers[parent].when) { // swap the two TimerTask tmp = timers[current]; timers[current] = timers[parent]; timers[parent] = tmp; // update pos and current current = parent; parent = (current - 1) / 2; } } private void DownHeap(int pos) { int current = pos; int child = 2 * current + 1; while (child < size && size > 0) { // compare the children if they exist if (child + 1 < size && timers[child + 1].when < timers[child].when) { child++; } // compare selected child with parent if (timers[current].when < timers[child].when) { break; } // swap the two TimerTask tmp = timers[current]; timers[current] = timers[child]; timers[child] = tmp; // update pos and current current = child; child = 2 * current + 1; } } public void Reset() { timers = new TimerTask[DEFAULT_HEAP_SIZE]; size = 0; } public void AdjustMinimum() { DownHeap(0); } public void DeleteIfCancelled() { for (int i = 0; i < size; i++) { if (timers[i].cancelled) { deletedCancelledNumber++; Delete(i); // re-try this point i--; } } } internal int GetTask(TimerTask task) { for (int i = 0; i < timers.Length; i++) { if (timers[i] == task) { return i; } } return -1; } } #endregion #region TimerEx Task Runner Implementation private sealed class TimerImpl { private bool cancelled; private bool finished; private String name; private TimerHeap tasks = new TimerHeap(); private System.Threading.Thread runner; private object syncRoot = new object(); public TimerImpl(String name, bool isBackground) { this.name = name; this.runner = new Thread(new ThreadStart(this.Run)); this.runner.Name = name; this.runner.IsBackground = isBackground; this.runner.Start(); } public String Name { get { return this.name; } } public object SyncRoot { get { return this.syncRoot; } } public bool Cancelled { get { return this.cancelled; } } public bool Finished { set { this.finished = value; } } /// /// Run this Timers event loop in its own Thread. /// public void Run() { while (true) { TimerTask task; lock (this.syncRoot) { // need to check cancelled inside the synchronized block if (cancelled) { return; } if (tasks.IsEmpty()) { if (finished) { return; } // no tasks scheduled -- sleep until any task appear try { Monitor.Wait(this.syncRoot); } catch (ThreadInterruptedException) { } continue; } DateTime currentTime = DateTime.Now; task = tasks.Minimum(); TimeSpan timeToSleep; lock (task.syncRoot) { if (task.cancelled) { tasks.Delete(0); continue; } // check the time to sleep for the first task scheduled timeToSleep = task.when - currentTime; } if (timeToSleep.CompareTo(TimeSpan.Zero) > 0) { // sleep! try { Monitor.Wait(this.syncRoot, timeToSleep); } catch (ThreadInterruptedException) { } continue; } // no sleep is necessary before launching the task lock (task.syncRoot) { int pos = 0; if (tasks.Minimum().when != task.when) { pos = tasks.GetTask(task); } if (task.cancelled) { tasks.Delete(tasks.GetTask(task)); continue; } // set time to schedule task.ScheduledTime = task.when; // remove task from queue tasks.Delete(pos); // set when the next task should be launched if (task.period.CompareTo(TimeSpan.Zero) >= 0) { // this is a repeating task, if (task.fixedRate) { // task is scheduled at fixed rate task.when = task.when + task.period; } else { // task is scheduled at fixed delay task.when = DateTime.Now + task.period; } // insert this task into queue InsertTask(task); } else { task.when = DateTime.MinValue; } } } bool taskCompletedNormally = false; try { task.Run(); taskCompletedNormally = true; } finally { if (!taskCompletedNormally) { lock (this) { cancelled = true; } } } } } public void InsertTask(TimerTask newTask) { // callers are synchronized tasks.Insert(newTask); Monitor.Pulse(this.syncRoot); } public void Cancel() { lock(this.syncRoot) { cancelled = true; tasks.Reset(); Monitor.Pulse(this.syncRoot); } } public int Purge() { if (tasks.IsEmpty()) { return 0; } // callers are synchronized tasks.deletedCancelledNumber = 0; tasks.DeleteIfCancelled(); return tasks.deletedCancelledNumber; } } #endregion #region Helper class to handle Timer shutdown when Disposed private sealed class DisposeHelper : IDisposable { private readonly TimerImpl impl; public DisposeHelper(TimerImpl impl) { this.impl = impl; } public void Dispose() { lock(impl.SyncRoot) { impl.Finished = true; Monitor.PulseAll(impl.SyncRoot); } } } #endregion } }