blob: f28afdf41f1520f6756ea40568b95d89398bdf4b [file] [log] [blame]
/*
* 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
{
/// <summary>
/// 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.
/// </summary>
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)
{
}
/// <summary>
/// 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.
/// </summary>
public void Cancel()
{
this.impl.Cancel();
}
/// <summary>
/// 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.
/// </summary>
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
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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
/// <summary>
/// Schedules the specified TimerTask for execution at the specified time. If the
/// time is in the past.
/// </summary>
public void Schedule(TimerTask task, DateTime when)
{
TimeSpan delay = when - DateTime.Now;
DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false);
}
/// <summary>
/// Schedules the specified TimerTask for execution after the specified delay.
/// </summary>
public void Schedule(TimerTask task, int delay)
{
if(delay < 0)
{
throw new ArgumentOutOfRangeException();
}
DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(-1), false);
}
/// <summary>
/// Schedules the specified TimerTask for execution after the specified delay.
/// </summary>
public void Schedule(TimerTask task, TimeSpan delay)
{
if(delay.CompareTo(TimeSpan.Zero) < 0)
{
throw new ArgumentOutOfRangeException();
}
DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false);
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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; }
}
/// <summary>
/// Run this Timers event loop in its own Thread.
/// </summary>
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
}
}