You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ta...@apache.org on 2013/04/25 00:17:49 UTC
svn commit: r1471736 - in
/activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src:
main/csharp/Threads/TimerEx.cs main/csharp/Threads/TimerTask.cs
test/csharp/Threads/TimerExTest.cs
Author: tabish
Date: Wed Apr 24 22:17:49 2013
New Revision: 1471736
URL: http://svn.apache.org/r1471736
Log:
Adds a new TimerEx class that implements a more scalable and controllable Timer object than the basic .NET version. Can be used to schedule multiple tasks that are run from a single Thread. Individual tasks can be cancelled as needed and tasks can be scheduled to repeat using either fixed rate or fixed delay execution.
Added:
activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerEx.cs (with props)
activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerTask.cs (with props)
activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/test/csharp/Threads/TimerExTest.cs (with props)
Added: activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerEx.cs
URL: http://svn.apache.org/viewvc/activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerEx.cs?rev=1471736&view=auto
==============================================================================
--- activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerEx.cs (added)
+++ activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerEx.cs Wed Apr 24 22:17:49 2013
@@ -0,0 +1,809 @@
+/*
+ * 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
+{
+ /// <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
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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
+
+ public void Schedule(TimerTask task, DateTime when)
+ {
+ TimeSpan delay = when - DateTime.Now;
+ DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false);
+ }
+
+ public void Schedule(TimerTask task, int delay)
+ {
+ if(delay < 0)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ DoScheduleImpl(task, TimeSpan.FromMilliseconds(delay), TimeSpan.FromMilliseconds(-1), false);
+ }
+
+ public void Schedule(TimerTask task, TimeSpan delay)
+ {
+ if(delay.CompareTo(TimeSpan.Zero) < 0)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ DoScheduleImpl(task, delay, TimeSpan.FromMilliseconds(-1), false);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ 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
+ }
+}
+
Propchange: activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerEx.cs
------------------------------------------------------------------------------
svn:eol-style = native
Added: activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerTask.cs
URL: http://svn.apache.org/viewvc/activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerTask.cs?rev=1471736&view=auto
==============================================================================
--- activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerTask.cs (added)
+++ activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerTask.cs Wed Apr 24 22:17:49 2013
@@ -0,0 +1,100 @@
+/*
+ * 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
+{
+ /// <summary>
+ /// A Task that is run in a Timer instance either once or repeatedly.
+ /// </summary>
+ public abstract class TimerTask
+ {
+ internal object syncRoot = new object();
+
+ internal DateTime when = DateTime.MinValue;
+ internal DateTime scheduledTime = DateTime.MinValue;
+ internal TimeSpan period;
+ internal bool cancelled;
+ internal bool fixedRate;
+
+ protected TimerTask()
+ {
+ }
+
+ public bool Cancel()
+ {
+ lock(this.syncRoot)
+ {
+ bool willRun = !cancelled && when != DateTime.MinValue;
+ cancelled = true;
+ return willRun;
+ }
+ }
+
+ public DateTime ScheduledExecutionTime
+ {
+ get
+ {
+ lock(this.syncRoot)
+ {
+ return this.scheduledTime;
+ }
+ }
+ }
+
+ public abstract void Run();
+
+ #region Timer Methods
+
+ internal DateTime When
+ {
+ get
+ {
+ lock(this.syncRoot)
+ {
+ return this.when;
+ }
+ }
+ }
+
+ internal DateTime ScheduledTime
+ {
+ set
+ {
+ lock(this.syncRoot)
+ {
+ this.scheduledTime = value;
+ }
+ }
+ }
+
+ internal bool IsScheduled
+ {
+ get
+ {
+ lock(this.syncRoot)
+ {
+ return when != DateTime.MinValue || scheduledTime != DateTime.MinValue;
+ }
+ }
+ }
+
+ #endregion
+ }
+}
+
Propchange: activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/main/csharp/Threads/TimerTask.cs
------------------------------------------------------------------------------
svn:eol-style = native
Added: activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/test/csharp/Threads/TimerExTest.cs
URL: http://svn.apache.org/viewvc/activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/test/csharp/Threads/TimerExTest.cs?rev=1471736&view=auto
==============================================================================
--- activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/test/csharp/Threads/TimerExTest.cs (added)
+++ activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/test/csharp/Threads/TimerExTest.cs Wed Apr 24 22:17:49 2013
@@ -0,0 +1,1565 @@
+/*
+ * 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;
+using Apache.NMS.Util;
+using Apache.NMS.ActiveMQ.Threads;
+using NUnit.Framework;
+
+namespace Apache.NMS.ActiveMQ.Test
+{
+ [TestFixture]
+ public class TimerExTest
+ {
+ TestData data;
+
+ class TestData
+ {
+ public object sync = new object();
+ public int timerCounter = 0;
+ }
+
+ class TimerTestTask : TimerTask
+ {
+ int wasRun = 0;
+
+ // Should we sleep for 200 ms each run()?
+ bool sleepInRun = false;
+
+ // Should we increment the timerCounter?
+ bool incrementCount = false;
+
+ // Should we terminate the timer at a specific timerCounter?
+ int terminateCount = -1;
+
+ // The timer we belong to
+ TimerEx timer = null;
+
+ TestData data;
+
+ public TimerTestTask(TestData data)
+ {
+ this.data = data;
+ }
+
+ public TimerTestTask(TimerEx t, TestData data)
+ {
+ this.timer = t;
+ this.data = data;
+ }
+
+ public override void Run()
+ {
+ lock(this)
+ {
+ wasRun++;
+ }
+ if (incrementCount)
+ {
+ data.timerCounter++;
+ }
+ if (terminateCount == data.timerCounter && timer != null)
+ {
+ timer.Cancel();
+ }
+ if (sleepInRun)
+ {
+ try
+ {
+ Thread.Sleep(200);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+
+ lock(data.sync)
+ {
+ Monitor.Pulse(data.sync);
+ }
+ }
+
+ public int WasRun()
+ {
+ lock(this)
+ {
+ return wasRun;
+ }
+ }
+
+ public void SleepInRun(bool sleepInRun)
+ {
+ this.sleepInRun = sleepInRun;
+ }
+
+ public void IncrementCount(bool incrementCount)
+ {
+ this.incrementCount = incrementCount;
+ }
+
+ public void TerminateCount(int terminateCount)
+ {
+ this.terminateCount = terminateCount;
+ }
+ }
+
+ private void WaitCallbackTask(object arg)
+ {
+ TimerTestTask task = arg as TimerTestTask;
+ task.Run();
+ }
+
+ class SlowThenFastTask : TimerTask
+ {
+ int wasRun = 0;
+ DateTime startedAt;
+ TimeSpan lastDelta;
+
+ public override void Run()
+ {
+ if (wasRun == 0)
+ {
+ startedAt = DateTime.Now;
+ }
+ lastDelta = DateTime.Now -
+ (startedAt + (TimeSpan.FromMilliseconds(100 * wasRun)));
+ wasRun++;
+ if (wasRun == 2)
+ {
+ try
+ {
+ Thread.Sleep(200);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+ }
+
+ public TimeSpan LastDelta
+ {
+ get { return lastDelta; }
+ }
+
+ public int WasRun()
+ {
+ return wasRun;
+ }
+ }
+
+ private void Sleep(int milliseconds)
+ {
+ try
+ {
+ Thread.Sleep(milliseconds);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+
+ private void Wait(int milliseconds)
+ {
+ lock (data.sync)
+ {
+ try
+ {
+ Monitor.Wait(data.sync, milliseconds);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+ }
+
+ [SetUp]
+ public void SetUp()
+ {
+ this.data = new TestData();
+ }
+
+ [Test]
+ public void TestConstructorBool()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx(true);
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(200));
+ Wait(1000);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.Run() method not called after 200ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestConstructor()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(200));
+ Wait(1000);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.Run() method not called after 200ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestConstructorStringBool()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx("TestConstructorStringBool", true);
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(200));
+ Wait(1000);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.Run() method not called after 200ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestConstructorString()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx("TestConstructorString");
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(200));
+ Wait(1000);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.Run() method not called after 200ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestConstructorThrowsException()
+ {
+ try
+ {
+ new TimerEx(null, true);
+ Assert.Fail("NullReferenceException expected");
+ }
+ catch (NullReferenceException)
+ {
+ //expected
+ }
+
+ try
+ {
+ new TimerEx(null, false);
+ Assert.Fail("NullReferenceException expected");
+ }
+ catch (NullReferenceException)
+ {
+ //expected
+ }
+ }
+
+ [Test]
+ public void TestCancel()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(200));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception, "Scheduling a task after Timer.Cancel() should throw exception");
+
+ // Ensure a task is run but not after cancel
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(500));
+ Wait(1000);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+ Wait(500);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method should not have been called after cancel");
+
+ // Ensure you can call cancel more than once
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(500));
+ Wait(500);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+ t.Cancel();
+ t.Cancel();
+ Wait(500);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method should not have been called after cancel");
+
+ // Ensure that a call to cancel from within a timer ensures no more
+ // run
+ t = new TimerEx();
+ testTask = new TimerTestTask(t, data);
+ testTask.IncrementCount(true);
+ testTask.TerminateCount(5); // Terminate after 5 runs
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100));
+ lock (data.sync)
+ {
+ try
+ {
+ Monitor.Wait(data.sync, 500);
+ Monitor.Wait(data.sync, 500);
+ Monitor.Wait(data.sync, 500);
+ Monitor.Wait(data.sync, 500);
+ Monitor.Wait(data.sync, 500);
+ Monitor.Wait(data.sync, 500);
+ }
+ catch (ThreadInterruptedException)
+ {
+ }
+ }
+ Assert.AreEqual(5, testTask.WasRun(),
+ "TimerTask.run() method should be called 5 times not " + testTask.WasRun());
+ t.Cancel();
+ Sleep(200);
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestPurge()
+ {
+ TimerEx t = null;
+ try
+ {
+ t = new TimerEx();
+ Assert.AreEqual(0, t.Purge());
+
+ TimerTestTask[] tasks = new TimerTestTask[100];
+ int[] delayTime = { 50, 80, 20, 70, 40, 10, 90, 30, 60 };
+
+ int j = 0;
+ for (int i = 0; i < 100; i++)
+ {
+ tasks[i] = new TimerTestTask(data);
+ t.Schedule(tasks[i], TimeSpan.FromMilliseconds(delayTime[j++]), TimeSpan.FromMilliseconds(200));
+ if (j == 9)
+ {
+ j = 0;
+ }
+ }
+
+ for (int i = 0; i < 50; i++)
+ {
+ tasks[i].Cancel();
+ }
+
+ Assert.IsTrue(t.Purge() <= 50);
+ Assert.AreEqual(0, t.Purge());
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleWaitCallbackDateTime()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.Schedule(callback, testTask, d);
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after Timer.Cancel() should throw exception");
+
+ // Ensure that the returned task from the WaitCallback schedule method
+ // cancel the task and prevent it from being run.
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(200);
+ TimerTask scheduled = t.Schedule(callback, testTask, d);
+ scheduled.Cancel();
+ Sleep(1000);
+ Assert.AreEqual(0, testTask.WasRun(), "Cancelled task shouldn't have run.");
+
+ // Ensure a TimerEx throws a ArgumentNullException if the task is null
+ t = new TimerEx();
+ exception = false;
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ try
+ {
+ t.Schedule(null, testTask, d);
+ }
+ catch (ArgumentNullException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task should throw ArgumentNullException");
+ t.Cancel();
+
+ // Ensure a task is run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(200);
+ t.Schedule(callback, testTask, d);
+ Sleep(400);
+
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Schedule(callback, testTask, d);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(150);
+ t.Schedule(callback, testTask, d);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(70);
+ t.Schedule(callback, testTask, d);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(10);
+ t.Schedule(callback, testTask, d);
+ Sleep(400);
+
+ Assert.AreEqual(4, data.timerCounter,
+ "Multiple tasks should have incremented counter 4 times not " + data.timerCounter);
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleWaitCallbackWithDelay()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ t.Schedule(callback, testTask, 200);
+ Sleep(1000);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleWaitCallbackWithDelayAsTimeSpan()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ t.Schedule(callback, testTask, TimeSpan.FromMilliseconds(200));
+ Sleep(1000);
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleWaitCallbackWithDelayAndPeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ t.Schedule(callback, testTask, 200, 100);
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleWaitCallbackWithDelayAndPeriodTimeSpan()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ t.Schedule(callback, testTask, TimeSpan.FromMilliseconds(200), TimeSpan.FromMilliseconds(100));
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleWaitCallbackWithDateTimePeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Schedule(callback, testTask, d, 100);
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleWaitCallbackWithDateTimePeriodTimeSpan()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Schedule(callback, testTask, d, TimeSpan.FromMilliseconds(100));
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleAtFixedRateWaitCallbackWithDelayPeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ t.ScheduleAtFixedRate(callback, testTask, 200, 100);
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleAtFixedRateWaitCallbackWithDelayPeriodTimeSpan()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ t.ScheduleAtFixedRate(
+ callback, testTask, TimeSpan.FromMilliseconds(200), TimeSpan.FromMilliseconds(100));
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleAtFixedRateWaitCallbackWithDateTimePeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.ScheduleAtFixedRate(callback, testTask, d, 100);
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleAtFixedRateWaitCallbackWithDateTimePeriodTimeSpan()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a task is run
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ WaitCallback callback = new WaitCallback(WaitCallbackTask);
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.ScheduleAtFixedRate(callback, testTask, d, TimeSpan.FromMilliseconds(100));
+ Sleep(1000);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method not called at least twice after 1 second sleep");
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleTimerTaskDateTime()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.Schedule(testTask, d);
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after Timer.Cancel() should throw exception");
+
+ // Ensure a TimerEx throws an InvalidOperationException if task already cancelled
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ testTask.Cancel();
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, d);
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after cancelling it should throw exception");
+ t.Cancel();
+
+ // Ensure a TimerEx throws a ArgumentNullException if the task is null
+ t = new TimerEx();
+ exception = false;
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ try
+ {
+ t.Schedule(null, d);
+ }
+ catch (ArgumentNullException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task should throw ArgumentNullException");
+ t.Cancel();
+
+ // Ensure a task is run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(200);
+ t.Schedule(testTask, d);
+ Sleep(400);
+
+ Assert.AreEqual(1, testTask.WasRun(), "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Schedule(testTask, d);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(150);
+ t.Schedule(testTask, d);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(70);
+ t.Schedule(testTask, d);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(10);
+ t.Schedule(testTask, d);
+ Sleep(400);
+
+ Assert.AreEqual(4, data.timerCounter,
+ "Multiple tasks should have incremented counter 4 times not " + data.timerCounter);
+ t.Cancel();
+ }
+ finally
+ {
+ if (t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleTimerTaskDelay()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.Schedule(testTask, 100);
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after Timer.Cancel() should throw exception");
+
+ // Ensure a TimerEx throws an InvalidOperationException if task already
+ // cancelled
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.Cancel();
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, 100);
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after cancelling it should throw exception");
+ t.Cancel();
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if delay is
+ // negative
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, -100);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task with negative delay should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a TimerEx throws a ArgumentNullException if the task is null
+ t = new TimerEx();
+ exception = false;
+ try
+ {
+ t.Schedule(null, 10);
+ }
+ catch (ArgumentNullException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task should throw ArgumentNullException");
+ t.Cancel();
+
+ // Ensure proper sequence of exceptions
+ t = new TimerEx();
+ exception = false;
+ try
+ {
+ t.Schedule(null, -10);
+ }
+ catch (ArgumentNullException)
+ {
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task with negative delays should throw IllegalArgumentException first");
+ t.Cancel();
+
+ // Ensure a task is run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ t.Schedule(testTask, 200);
+ Sleep(400);
+ Assert.AreEqual(1, testTask.WasRun(),
+ "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, 100);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, 150);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, 70);
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, 10);
+ Sleep(400);
+ Assert.AreEqual(4, data.timerCounter,
+ "Multiple tasks should have incremented counter 4 times not " + data.timerCounter);
+ t.Cancel();
+ }
+ finally
+ {
+ if(t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleTimerTaskDelayTimeSpan()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after Timer.Cancel() should throw exception");
+
+ // Ensure a TimerEx throws an InvalidOperationException if task already
+ // cancelled
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.Cancel();
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after cancelling it should throw exception");
+ t.Cancel();
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if delay is
+ // negative
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(-100));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task with negative delay should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a TimerEx throws a ArgumentNullException if the task is null
+ t = new TimerEx();
+ exception = false;
+ try
+ {
+ t.Schedule(null, TimeSpan.FromMilliseconds(10));
+ }
+ catch (ArgumentNullException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task should throw ArgumentNullException");
+ t.Cancel();
+
+ // Ensure proper sequence of exceptions
+ t = new TimerEx();
+ exception = false;
+ try
+ {
+ t.Schedule(null, TimeSpan.FromMilliseconds(-10));
+ }
+ catch (ArgumentNullException)
+ {
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task with negative delays should throw IllegalArgumentException first");
+ t.Cancel();
+
+ // Ensure a task is run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(200));
+ Sleep(400);
+ Assert.AreEqual(1, testTask.WasRun(),
+ "TimerTask.run() method not called after 200ms");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100));
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(150));
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(70));
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(10));
+ Sleep(400);
+ Assert.AreEqual(4, data.timerCounter,
+ "Multiple tasks should have incremented counter 4 times not " + data.timerCounter);
+ t.Cancel();
+ }
+ finally
+ {
+ if(t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleTimerTaskDelayPeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after Timer.Cancel() should throw exception");
+
+ // Ensure a TimerEx throws an InvalidOperationException if task already
+ // cancelled
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.Cancel();
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after cancelling it should throw exception");
+ t.Cancel();
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if delay is negative
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(-100), TimeSpan.FromMilliseconds(100));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task with negative delay should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if period is negative
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(-100));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task with negative period should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if period is
+ // zero
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(0));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task with 0 period should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a TimerEx throws a ArgumentNullException if the task is null
+ t = new TimerEx();
+ exception = false;
+ try
+ {
+ t.Schedule(null, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(10));
+ }
+ catch (ArgumentNullException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task should throw ArgumentNullException");
+ t.Cancel();
+
+ // Ensure proper sequence of exceptions
+ t = new TimerEx();
+ exception = false;
+ try
+ {
+ t.Schedule(null, TimeSpan.FromMilliseconds(-10), TimeSpan.FromMilliseconds(-10));
+ }
+ catch (ArgumentNullException)
+ {
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task with negative delays should throw ArgumentOutOfRangeException first");
+ t.Cancel();
+
+ // Ensure a task is run at least twice
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100));
+ Sleep(400);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method should have been called at least twice (" + testTask.WasRun() + ")");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100)); // at least 9 times
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(200), TimeSpan.FromMilliseconds(100)); // at least 7 times
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(300), TimeSpan.FromMilliseconds(200)); // at least 4 times
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ t.Schedule(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(200)); // at least 4 times
+ Sleep(1200);
+ Assert.IsTrue(data.timerCounter >= 24,
+ "Multiple tasks should have incremented counter 24 times not " + data.timerCounter);
+ t.Cancel();
+ }
+ finally
+ {
+ if(t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleTimerTaskDateTimePeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after Timer.Cancel() should throw exception");
+
+ // Ensure a TimerEx throws an InvalidOperationException if task already cancelled
+ t = new TimerEx();
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ testTask = new TimerTestTask(data);
+ testTask.Cancel();
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task after cancelling it should throw exception");
+ t.Cancel();
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if period is
+ // negative
+ t = new TimerEx();
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(-100));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a task with negative period should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a TimerEx throws a ArgumentNullException if the task is null
+ t = new TimerEx();
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ exception = false;
+ try
+ {
+ t.Schedule(null, d, TimeSpan.FromMilliseconds(10));
+ }
+ catch (ArgumentNullException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "Scheduling a null task should throw ArgumentNullException");
+ t.Cancel();
+
+ // Ensure a task is run at least twice
+ t = new TimerEx();
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ testTask = new TimerTestTask(data);
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(100));
+ Sleep(800);
+ Assert.IsTrue( testTask.WasRun() >= 2,
+ "TimerTask.Run() method should have been called at least twice (" + testTask.WasRun() + ")");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(100)); // at least 9 times
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(200);
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(100)); // at least 7 times
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(300);
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(200)); // at least 4 times
+ testTask = new TimerTestTask(data);
+ testTask.IncrementCount(true);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(400);
+ t.Schedule(testTask, d, TimeSpan.FromMilliseconds(200)); // at least 4 times
+ Sleep(3000);
+ Assert.IsTrue(data.timerCounter >= 24,
+ "Multiple tasks should have incremented counter 24 times not " + data.timerCounter);
+ t.Cancel();
+ }
+ finally
+ {
+ if(t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleAtFixedRateTimerTaskDelayPeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Cancel();
+ bool exception = false;
+ try
+ {
+ t.ScheduleAtFixedRate(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "scheduleAtFixedRate after Timer.Cancel() should throw exception");
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if delay is
+ // negative
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.ScheduleAtFixedRate(testTask, TimeSpan.FromMilliseconds(-100), TimeSpan.FromMilliseconds(100));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "scheduleAtFixedRate with negative delay should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a TimerEx throws an ArgumentOutOfRangeException if period is
+ // negative
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.ScheduleAtFixedRate(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(-100));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "scheduleAtFixedRate with negative period should throw IllegalArgumentException");
+ t.Cancel();
+
+ // Ensure a task is run at least twice
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ t.ScheduleAtFixedRate(testTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100));
+ Sleep(400);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method should have been called at least twice (" + testTask.WasRun() + ")");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ SlowThenFastTask slowThenFastTask = new SlowThenFastTask();
+
+ // at least 9 times even when asleep
+ t.ScheduleAtFixedRate(slowThenFastTask, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(100));
+ Sleep(1000);
+ long lastDelta = (long) slowThenFastTask.LastDelta.TotalMilliseconds;
+ Assert.IsTrue(lastDelta < 300,
+ "Fixed Rate Schedule should catch up, but is off by " + lastDelta + " ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if(t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+
+ [Test]
+ public void TestScheduleAtFixedRateTimerTaskDateTimePeriod()
+ {
+ TimerEx t = null;
+ try
+ {
+ // Ensure a TimerEx throws an InvalidOperationException after cancelled
+ t = new TimerEx();
+ TimerTestTask testTask = new TimerTestTask(data);
+ t.Cancel();
+ bool exception = false;
+ DateTime d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ try
+ {
+ t.ScheduleAtFixedRate(testTask, d, TimeSpan.FromMilliseconds(100));
+ }
+ catch (InvalidOperationException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "scheduleAtFixedRate after Timer.Cancel() should throw exception");
+
+ // Ensure a TimerEx throws an IllegalArgumentException if period is
+ // negative
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ exception = false;
+ try
+ {
+ t.ScheduleAtFixedRate(testTask, d, TimeSpan.FromMilliseconds(-100));
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ exception = true;
+ }
+ Assert.IsTrue(exception,
+ "scheduleAtFixedRate with negative period should throw ArgumentOutOfRangeException");
+ t.Cancel();
+
+ // Ensure a task is run at least twice
+ t = new TimerEx();
+ testTask = new TimerTestTask(data);
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+ t.ScheduleAtFixedRate(testTask, d, TimeSpan.FromMilliseconds(100));
+ Sleep(400);
+ Assert.IsTrue(testTask.WasRun() >= 2,
+ "TimerTask.run() method should have been called at least twice (" + testTask.WasRun() + ")");
+ t.Cancel();
+
+ // Ensure multiple tasks are run
+ t = new TimerEx();
+ SlowThenFastTask slowThenFastTask = new SlowThenFastTask();
+ d = DateTime.Now + TimeSpan.FromMilliseconds(100);
+
+ // at least 9 times even when asleep
+ t.ScheduleAtFixedRate(slowThenFastTask, d, TimeSpan.FromMilliseconds(100));
+ Sleep(1000);
+ long lastDelta = (long) slowThenFastTask.LastDelta.TotalMilliseconds;
+ Assert.IsTrue(lastDelta < 300,
+ "Fixed Rate Schedule should catch up, but is off by " + lastDelta + " ms");
+ t.Cancel();
+ }
+ finally
+ {
+ if(t != null)
+ {
+ t.Cancel();
+ }
+ }
+ }
+ }
+}
+
Propchange: activemq/activemq-dotnet/Apache.NMS.ActiveMQ/trunk/src/test/csharp/Threads/TimerExTest.cs
------------------------------------------------------------------------------
svn:eol-style = native