feat: add thread safety to error timer management in StatusMonitorBase

This commit is contained in:
Neil Dorin 2026-07-22 09:04:43 -06:00
parent 42b358862f
commit 4252e9c8e6

View file

@ -87,6 +87,14 @@ namespace PepperDash.Essentials.Core
long ErrorTime; long ErrorTime;
Timer WarningTimer; Timer WarningTimer;
Timer ErrorTimer; Timer ErrorTimer;
// Guards WarningTimer/ErrorTimer against concurrent access - StartErrorTimers/StopErrorTimers/
// ResetErrorTimers can each be invoked from different threads (e.g. a socket data-received
// callback vs. a connection-status-changed callback), and without this lock a null-check followed
// by a dereference (e.g. in ResetErrorTimers) can race with StopErrorTimers nulling the field in
// between, throwing an unhandled NullReferenceException on a background thread that crashes the
// whole program.
private readonly object _timerLock = new object();
/// Constructor /// Constructor
/// </summary> /// </summary>
/// <param name="parent">parent device</param> /// <param name="parent">parent device</param>
@ -154,6 +162,8 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
protected void StartErrorTimers() protected void StartErrorTimers()
{ {
lock (_timerLock)
{
if (WarningTimer == null) if (WarningTimer == null)
{ {
WarningTimer = new Timer(WarningTime) { AutoReset = false }; WarningTimer = new Timer(WarningTime) { AutoReset = false };
@ -167,16 +177,20 @@ namespace PepperDash.Essentials.Core
ErrorTimer.Start(); ErrorTimer.Start();
} }
} }
}
/// <summary> /// <summary>
/// Stops the error timers /// Stops the error timers
/// </summary> /// </summary>
protected void StopErrorTimers() protected void StopErrorTimers()
{ {
if (WarningTimer != null) WarningTimer.Stop(); lock (_timerLock)
if (ErrorTimer != null) ErrorTimer.Stop(); {
WarningTimer = null; if (WarningTimer != null) WarningTimer.Stop();
ErrorTimer = null; if (ErrorTimer != null) ErrorTimer.Stop();
WarningTimer = null;
ErrorTimer = null;
}
} }
/// <summary> /// <summary>
@ -184,6 +198,8 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
protected void ResetErrorTimers() protected void ResetErrorTimers()
{ {
lock (_timerLock)
{
if (WarningTimer != null) if (WarningTimer != null)
{ {
WarningTimer.Stop(); WarningTimer.Stop();
@ -196,6 +212,7 @@ namespace PepperDash.Essentials.Core
ErrorTimer.Interval = ErrorTime; ErrorTimer.Interval = ErrorTime;
ErrorTimer.Start(); ErrorTimer.Start();
} }
}
} }
} }
} }