using System;
using System.Threading;
using Abp.Dependency;
namespace Abp.Threading.Timers
{
///
/// A roboust timer implementation that ensures no overlapping occurs. It waits exactly specified between ticks.
///
//TODO: Extract interface or make all members virtual to make testing easier.
public class AbpTimer : RunnableBase, ITransientDependency
{
///
/// This event is raised periodically according to Period of Timer.
///
public event EventHandler Elapsed;
///
/// Task period of timer (as milliseconds).
///
public int Period { get; set; }
///
/// Indicates whether timer raises Elapsed event on Start method of Timer for once.
/// Default: False.
///
public bool RunOnStart { get; set; }
///
/// This timer is used to perfom the task at spesified intervals.
///
private readonly Timer _taskTimer;
///
/// Indicates that whether timer is running or stopped.
///
private volatile bool _running;
///
/// Indicates that whether performing the task or _taskTimer is in sleep mode.
/// This field is used to wait executing tasks when stopping Timer.
///
private volatile bool _performingTasks;
///
/// Creates a new Timer.
///
public AbpTimer()
{
_taskTimer = new Timer(TimerCallBack, null, Timeout.Infinite, Timeout.Infinite);
}
///
/// Creates a new Timer.
///
/// Task period of timer (as milliseconds)
/// Indicates whether timer raises Elapsed event on Start method of Timer for once
public AbpTimer(int period, bool runOnStart = false)
: this()
{
Period = period;
RunOnStart = runOnStart;
}
///
/// Starts the timer.
///
public override void Start()
{
if (Period <= 0)
{
throw new AbpException("Period should be set before starting the timer!");
}
base.Start();
_running = true;
_taskTimer.Change(RunOnStart ? 0 : Period, Timeout.Infinite);
}
///
/// Stops the timer.
///
public override void Stop()
{
lock (_taskTimer)
{
_running = false;
_taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
}
base.Stop();
}
///
/// Waits the service to stop.
///
public override void WaitToStop()
{
lock (_taskTimer)
{
while (_performingTasks)
{
Monitor.Wait(_taskTimer);
}
}
base.WaitToStop();
}
///
/// This method is called by _taskTimer.
///
/// Not used argument
private void TimerCallBack(object state)
{
lock (_taskTimer)
{
if (!_running || _performingTasks)
{
return;
}
_taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
_performingTasks = true;
}
try
{
if (Elapsed != null)
{
Elapsed(this, new EventArgs());
}
}
catch
{
}
finally
{
lock (_taskTimer)
{
_performingTasks = false;
if (_running)
{
_taskTimer.Change(Period, Timeout.Infinite);
}
Monitor.Pulse(_taskTimer);
}
}
}
}
}