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); } } } } }