Last active
February 14, 2023 18:30
-
-
Save randyburden/fe0febe315d7887b2471c7328a95fe5e to your computer and use it in GitHub Desktop.
Wraps System.Threading.Timer and guarantees non-overlapping executions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading; | |
namespace Helpers | |
{ | |
///<summary> | |
/// Provides a mechanism for executing a method at specified intervals without overlapping executions. | |
/// While the timer is executing the method/action, if another interval run occurs, it will return | |
/// immediately (skipping the new run) to prevent an overlapping execution and allow the previous execution to continue. | |
/// </summary> | |
public class NonOverlappingTimer : IDisposable | |
{ | |
private readonly Timer _timer; | |
private readonly object _lock = new object(); | |
/// <summary>Initializes a new instance of the Timer class, using <see cref="T:System.TimeSpan" /> values to measure time intervals.</summary> | |
/// <param name="action">The action to execute at the defined interval. </param> | |
/// <param name="dueTime">The amount of time to delay before invoking the action. Specify negative one (-1) milliseconds to prevent the timer from starting. Specify zero (0) to start the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of the action referenced by <paramref name="action" />. Specify negative one (-1) milliseconds to disable periodic signaling. </param> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The number of milliseconds in the value of <paramref name="dueTime" /> or <paramref name="period" /> is negative and not equal to <see cref="F:System.Threading.Timeout.Infinite" />, or is greater than <see cref="F:System.Int32.MaxValue" />. </exception> | |
/// <exception cref="T:System.ArgumentNullException">The <paramref name="action" /> parameter is null. </exception> | |
public NonOverlappingTimer(Action action, TimeSpan dueTime, TimeSpan period) | |
{ | |
if (action == null) | |
{ | |
throw new ArgumentNullException(nameof(action)); | |
} | |
TimerCallback callback = state => | |
{ | |
action(); | |
}; | |
var wrappedCallback = WrapCallback(callback); | |
_timer = new Timer(wrappedCallback, null, dueTime, period); | |
} | |
private TimerCallback WrapCallback(TimerCallback callback) | |
{ | |
TimerCallback wrappedCallback = state => | |
{ | |
// Overlapping calls will not acquire the lock and return immediately | |
if (!Monitor.TryEnter(_lock)) | |
{ | |
return; | |
} | |
try | |
{ | |
callback(state); | |
} | |
catch(Exception) | |
{ | |
// Ignore | |
} | |
finally | |
{ | |
Monitor.Exit(_lock); | |
} | |
}; | |
return wrappedCallback; | |
} | |
/// <summary>Initializes a new instance of the Timer class, using a 32-bit signed integer to specify the time interval.</summary> | |
/// <param name="callback">A <see cref="T:System.Threading.TimerCallback" /> delegate representing a method to be executed. </param> | |
/// <param name="state">An object containing information to be used by the callback method, or null. </param> | |
/// <param name="dueTime">The amount of time to delay before <paramref name="callback" /> is invoked, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to prevent the timer from starting. Specify zero (0) to start the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of <paramref name="callback" />, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to disable periodic signaling. </param> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="dueTime" /> or <paramref name="period" /> parameter is negative and is not equal to <see cref="F:System.Threading.Timeout.Infinite" />. </exception> | |
/// <exception cref="T:System.ArgumentNullException">The <paramref name="callback" /> parameter is null. </exception> | |
public NonOverlappingTimer(TimerCallback callback, object state, int dueTime, int period) | |
{ | |
var wrappedCallback = WrapCallback(callback); | |
_timer = new Timer(wrappedCallback, state, dueTime, period); | |
} | |
/// <summary>Initializes a new instance of the Timer class, using <see cref="T:System.TimeSpan" /> values to measure time intervals.</summary> | |
/// <param name="callback">A delegate representing a method to be executed. </param> | |
/// <param name="state">An object containing information to be used by the callback method, or null. </param> | |
/// <param name="dueTime">The amount of time to delay before the <paramref name="callback" /> parameter invokes its methods. Specify negative one (-1) milliseconds to prevent the timer from starting. Specify zero (0) to start the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of the methods referenced by <paramref name="callback" />. Specify negative one (-1) milliseconds to disable periodic signaling. </param> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The number of milliseconds in the value of <paramref name="dueTime" /> or <paramref name="period" /> is negative and not equal to <see cref="F:System.Threading.Timeout.Infinite" />, or is greater than <see cref="F:System.Int32.MaxValue" />. </exception> | |
/// <exception cref="T:System.ArgumentNullException">The <paramref name="callback" /> parameter is null. </exception> | |
public NonOverlappingTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period) | |
{ | |
var wrappedCallback = WrapCallback(callback); | |
_timer = new Timer(wrappedCallback, state, dueTime, period); | |
} | |
/// <summary>Initializes a new instance of the Timer class.</summary> | |
/// <param name="callback">A <see cref="T:System.Threading.TimerCallback" /> delegate representing a method to be executed. </param> | |
/// <param name="state">An object containing information to be used by the callback method, or null. </param> | |
/// <param name="dueTime">The amount of time to delay before <paramref name="callback" /> is invoked, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to prevent the timer from starting. Specify zero (0) to start the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of <paramref name="callback" />, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to disable periodic signaling. </param> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="dueTime" /> or <paramref name="period" /> parameter is negative and is not equal to <see cref="F:System.Threading.Timeout.Infinite" />. </exception> | |
/// <exception cref="T:System.ArgumentNullException">The <paramref name="callback" /> parameter is null. </exception> | |
public NonOverlappingTimer(TimerCallback callback, object state, uint dueTime, uint period) | |
{ | |
var wrappedCallback = WrapCallback(callback); | |
_timer = new Timer(wrappedCallback, state, dueTime, period); | |
} | |
/// <summary>Initializes a new instance of the Timer class.</summary> | |
/// <param name="callback">A <see cref="T:System.Threading.TimerCallback" /> delegate representing a method to be executed. </param> | |
/// <param name="state">An object containing information to be used by the callback method, or null. </param> | |
/// <param name="dueTime">The amount of time to delay before <paramref name="callback" /> is invoked, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to prevent the timer from starting. Specify zero (0) to start the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of <paramref name="callback" />, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to disable periodic signaling. </param> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="dueTime" /> or <paramref name="period" /> parameter is negative and is not equal to <see cref="F:System.Threading.Timeout.Infinite" />. </exception> | |
/// <exception cref="T:System.ArgumentNullException">The <paramref name="callback" /> parameter is null. </exception> | |
public NonOverlappingTimer(TimerCallback callback, object state, long dueTime, long period) | |
{ | |
var wrappedCallback = WrapCallback(callback); | |
_timer = new Timer(wrappedCallback, state, dueTime, period); | |
} | |
/// <summary>Initializes a new instance of the Timer class.</summary> | |
/// <param name="callback">A <see cref="T:System.Threading.TimerCallback" /> delegate representing a method to be executed. </param> | |
public NonOverlappingTimer(TimerCallback callback) | |
{ | |
var wrappedCallback = WrapCallback(callback); | |
_timer = new Timer(wrappedCallback); | |
} | |
/// <summary>Changes the start time and the interval between method invocations for a timer, using 32-bit signed integers to measure time intervals.</summary> | |
/// <returns>true if the timer was successfully updated; otherwise, false.</returns> | |
/// <param name="dueTime">The amount of time to delay before the invoking the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to prevent the timer from restarting. Specify zero (0) to restart the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to disable periodic signaling. </param> | |
/// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Timer" /> has already been disposed. </exception> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="dueTime" /> or <paramref name="period" /> parameter is negative and is not equal to <see cref="F:System.Threading.Timeout.Infinite" />. </exception> | |
/// <filterpriority>2</filterpriority> | |
public bool Change(int dueTime, int period) | |
{ | |
return _timer.Change(dueTime, period); | |
} | |
/// <summary>Changes the start time and the interval between method invocations for a timer.</summary> | |
/// <returns>true if the timer was successfully updated; otherwise, false.</returns> | |
/// <param name="dueTime">The amount of time to delay before the invoking the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to prevent the timer from restarting. Specify zero (0) to restart the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to disable periodic signaling. </param> | |
/// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Timer" /> has already been disposed. </exception> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="dueTime" /> or <paramref name="period" /> parameter is negative and is not equal to <see cref="F:System.Threading.Timeout.Infinite" />. </exception> | |
/// <filterpriority>2</filterpriority> | |
public bool Change(TimeSpan dueTime, TimeSpan period) | |
{ | |
return _timer.Change(dueTime, period); | |
} | |
/// <summary>Changes the start time and the interval between method invocations for a timer.</summary> | |
/// <returns>true if the timer was successfully updated; otherwise, false.</returns> | |
/// <param name="dueTime">The amount of time to delay before the invoking the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to prevent the timer from restarting. Specify zero (0) to restart the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to disable periodic signaling. </param> | |
/// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Timer" /> has already been disposed. </exception> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="dueTime" /> or <paramref name="period" /> parameter is negative and is not equal to <see cref="F:System.Threading.Timeout.Infinite" />. </exception> | |
/// <filterpriority>2</filterpriority> | |
public bool Change(uint dueTime, uint period) | |
{ | |
return _timer.Change(dueTime, period); | |
} | |
/// <summary>Changes the start time and the interval between method invocations for a timer.</summary> | |
/// <returns>true if the timer was successfully updated; otherwise, false.</returns> | |
/// <param name="dueTime">The amount of time to delay before the invoking the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to prevent the timer from restarting. Specify zero (0) to restart the timer immediately. </param> | |
/// <param name="period">The time interval between invocations of the callback method specified when the <see cref="T:System.Threading.Timer" /> was constructed, in milliseconds. Specify <see cref="F:System.Threading.Timeout.Infinite" /> to disable periodic signaling. </param> | |
/// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Timer" /> has already been disposed. </exception> | |
/// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="dueTime" /> or <paramref name="period" /> parameter is negative and is not equal to <see cref="F:System.Threading.Timeout.Infinite" />. </exception> | |
/// <filterpriority>2</filterpriority> | |
public bool Change(long dueTime, long period) | |
{ | |
return _timer.Change(dueTime, dueTime); | |
} | |
/// <summary> | |
/// Stop the timer. | |
/// </summary> | |
public void Stop() | |
{ | |
_timer.Change(Timeout.Infinite, Timeout.Infinite); | |
} | |
/// <summary>Releases all resources used by the current instance of <see cref="T:System.Threading.Timer" />.</summary> | |
/// <filterpriority>2</filterpriority> | |
public bool Dispose(WaitHandle notifyObject) | |
{ | |
return _timer.Dispose(notifyObject); | |
} | |
/// <summary>Releases all resources used by the current instance of <see cref="T:System.Threading.Timer" />.</summary> | |
/// <filterpriority>2</filterpriority> | |
public void Dispose() | |
{ | |
_timer.Dispose(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment