I've a class in a service that does interval based actions.
It uses a variable pollTimer
of type System.Timers.Timer and an event method executing the actions.
Given these event methods running from a thread pool, and the accessing of the timer itself can be from multiple threads, I want to protect access to it.
Is my basic idea below is sound enough?
Below is my basic idea for both Action and Func based access and usage.
(Note: Edit history shows the step by step changes as suggested in the comments)
private readonly object pollTimerLock = new object();
private Timer pollTimer;
private enum NullPollTimerFailureMode { Continue, Fail };
private void RunOnLockedPollTimer(NullPollTimerFailureMode nullPollTimerFailureMode, Action<Timer> action)
{
Timer timer = pollTimer;
if (null == timer)
{
if (NullPollTimerFailureMode.Fail == nullPollTimerFailureMode)
throw new GenericException<SimplePoll>("unexpected null pollTimer");
// else: NOP
}
else
{
lock (pollTimerLock)
action(timer);
}
}
private TResult RunOnLockedPolltimer<TResult>(TResult defaultResult, Func<Timer, TResult> action)
{
TResult result = defaultResult;
RunOnLockedPolltimer(NullPollTimerFailureMode.Continue,
lockedPolltimer =>
{
result = action(lockedPolltimer);
});
return result;
}
(methods are private right now, as it's internal for the class encapsulating the behaviour)
And some access patterns:
Obtaining state:
bool result = RunOnLockedPolltimer(false,
lockedPolltimer => lockedPolltimer.Enabled);
Disabling timer:
RunOnLockedPolltimer(NullPollTimerFailureMode.Continue,
lockedPolltimer =>
lockedPolltimer.Enabled = false);
Enabling timer:
RunOnLockedPolltimer(NullPollTimerFailureMode.Fail,
lockedPolltimer =>
{
lockedPolltimer.AutoReset = true;
lockedPolltimer.Enabled = true;
});
Event: should I use RunOnLockedPolltimer
here or not?
private void PollTimerElapsed(object sender, ElapsedEventArgs e)
{
try
{
if (!IsPolling)
return;
// Ensure only one event on the timer can run at the same time
RunOnLockedPollTimer(NullPollTimerFailureMode.Fail,
lockedPollTimer => LockedPollTimerElapsed());
}
catch (Exception exception)
{
try
{
Logger.LogException("PollTimerElapsed", InformationLevel.Critical, exception);
}
catch
{
// if logging fails, don't let exceptions exit the PollTimerElapsed eventhandler.
}
}
}