internal bool Fire()
{
if (_timerState == TimerState.Sentinel)
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "TimerQueue tried to Fire a Sentinel.");
}
if (_timerState != TimerState.Ready)
{
return true;
}
// Must get the current tick count within this method so it is guaranteed not to be before
// StartTime, which is set in the constructor.
int nowMilliseconds = Environment.TickCount;
if (IsTickBetween(StartTime, Expiration, nowMilliseconds))
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"TimerThreadTimer#{StartTime}::Fire() Not firing ({StartTime} <= {nowMilliseconds} < {Expiration})");
return false;
}
bool needCallback = false;
lock (_queueLock)
{
if (_timerState == TimerState.Ready)
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"TimerThreadTimer#{StartTime}::Fire() Firing ({StartTime} <= {nowMilliseconds} >= " + Expiration + ")");
_timerState = TimerState.Fired;
// Remove it from the list.
Next.Prev = Prev;
Prev.Next = Next;
Next = null;
Prev = null;
needCallback = _callback != null;
}
}
if (needCallback)
{
try
{
Callback callback = _callback;
object context = _context;
_callback = null;
_context = null;
callback(this, nowMilliseconds, context);
}
catch (Exception exception)
{
if (ExceptionCheck.IsFatal(exception))
throw;
if (NetEventSource.IsEnabled) NetEventSource.Error(this, $"exception in callback: {exception}");
// This thread is not allowed to go into user code, so we should never get an exception here.
// So, in debug, throw it up, killing the AppDomain. In release, we'll just ignore it.
#if DEBUG
throw;
#endif
}
}
return true;
}
}