From a857b78edccf9ceec7f06920e877e2d7d4a3c778 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:43:10 +0000 Subject: [PATCH 1/6] Initial plan From a2446cbf67734950d0484cb9492a438bdeafb1d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:45:34 +0000 Subject: [PATCH 2/6] Refactor UdpSourceBase to use Dispose pattern instead of manual disposal in finally block Co-authored-by: codegefluester <203914+codegefluester@users.noreply.github.com> --- GamesDat/Telemetry/Sources/UdpSourceBase.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/GamesDat/Telemetry/Sources/UdpSourceBase.cs b/GamesDat/Telemetry/Sources/UdpSourceBase.cs index b032040..e53f8fb 100644 --- a/GamesDat/Telemetry/Sources/UdpSourceBase.cs +++ b/GamesDat/Telemetry/Sources/UdpSourceBase.cs @@ -45,10 +45,15 @@ public override async IAsyncEnumerable ReadContinuousAsync([EnumeratorCancell finally { _isListening = false; - _listener.Dispose(); } } + public override void Dispose() + { + _listener?.Dispose(); + base.Dispose(); + } + abstract protected IEnumerable ProcessData(byte[] data); } } From 6d5b8e1da87519e076f13c71d3c4454804340b46 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:46:22 +0000 Subject: [PATCH 3/6] Add thread-safety to Dispose method in UdpSourceBase Co-authored-by: codegefluester <203914+codegefluester@users.noreply.github.com> --- GamesDat/Telemetry/Sources/UdpSourceBase.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/GamesDat/Telemetry/Sources/UdpSourceBase.cs b/GamesDat/Telemetry/Sources/UdpSourceBase.cs index e53f8fb..29d39e6 100644 --- a/GamesDat/Telemetry/Sources/UdpSourceBase.cs +++ b/GamesDat/Telemetry/Sources/UdpSourceBase.cs @@ -9,6 +9,8 @@ namespace GamesDat.Core.Telemetry.Sources { public abstract class UdpSourceBase : TelemetrySourceBase { + private readonly object _disposeLock = new object(); + private bool _disposed; protected UdpClient _listener; protected IPEndPoint _endpoint; @@ -50,7 +52,14 @@ public override async IAsyncEnumerable ReadContinuousAsync([EnumeratorCancell public override void Dispose() { - _listener?.Dispose(); + lock (_disposeLock) + { + if (!_disposed) + { + _listener?.Dispose(); + _disposed = true; + } + } base.Dispose(); } From 50797a7e94e6c1b22b0c03da8321b32176da410d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:47:00 +0000 Subject: [PATCH 4/6] Move base.Dispose() inside lock for better thread-safety Co-authored-by: codegefluester <203914+codegefluester@users.noreply.github.com> --- GamesDat/Telemetry/Sources/UdpSourceBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GamesDat/Telemetry/Sources/UdpSourceBase.cs b/GamesDat/Telemetry/Sources/UdpSourceBase.cs index 29d39e6..b48da0f 100644 --- a/GamesDat/Telemetry/Sources/UdpSourceBase.cs +++ b/GamesDat/Telemetry/Sources/UdpSourceBase.cs @@ -57,10 +57,10 @@ public override void Dispose() if (!_disposed) { _listener?.Dispose(); + base.Dispose(); _disposed = true; } } - base.Dispose(); } abstract protected IEnumerable ProcessData(byte[] data); From 11d98ff3b0b23b437eda24bc8b6c1691dbcfd88d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:47:30 +0000 Subject: [PATCH 5/6] Set disposed flag before disposal operations for better exception safety Co-authored-by: codegefluester <203914+codegefluester@users.noreply.github.com> --- GamesDat/Telemetry/Sources/UdpSourceBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GamesDat/Telemetry/Sources/UdpSourceBase.cs b/GamesDat/Telemetry/Sources/UdpSourceBase.cs index b48da0f..e5efa2d 100644 --- a/GamesDat/Telemetry/Sources/UdpSourceBase.cs +++ b/GamesDat/Telemetry/Sources/UdpSourceBase.cs @@ -56,9 +56,9 @@ public override void Dispose() { if (!_disposed) { + _disposed = true; _listener?.Dispose(); base.Dispose(); - _disposed = true; } } } From d3c5fe440e9705701b8ec33fa5c3fb596e9bad4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 22:48:01 +0000 Subject: [PATCH 6/6] Add try-finally to ensure base.Dispose() is always called Co-authored-by: codegefluester <203914+codegefluester@users.noreply.github.com> --- GamesDat/Telemetry/Sources/UdpSourceBase.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/GamesDat/Telemetry/Sources/UdpSourceBase.cs b/GamesDat/Telemetry/Sources/UdpSourceBase.cs index e5efa2d..0cca15f 100644 --- a/GamesDat/Telemetry/Sources/UdpSourceBase.cs +++ b/GamesDat/Telemetry/Sources/UdpSourceBase.cs @@ -57,8 +57,14 @@ public override void Dispose() if (!_disposed) { _disposed = true; - _listener?.Dispose(); - base.Dispose(); + try + { + _listener?.Dispose(); + } + finally + { + base.Dispose(); + } } } }