From 9c866cd837b1a8df4837b2f9ce933bf7f7df059f Mon Sep 17 00:00:00 2001 From: Brant Burnett Date: Mon, 29 Sep 2025 11:18:16 -0400 Subject: [PATCH 1/3] Add null checks Motivation ---------- Better exceptions with preconditions. Modifications ------------- Add a ThrowHelper for .NET < 6 and then use it or ArgumentNullException.ThrowIfNull. --- src/CenterEdge.Async/AsyncHelper.cs | 70 +++++++++++++++++++ src/CenterEdge.Async/CenterEdge.Async.csproj | 4 ++ .../ExclusiveSynchronizationContext.cs | 9 +++ src/CenterEdge.Async/ThrowHelper.cs | 9 +++ 4 files changed, 92 insertions(+) create mode 100644 src/CenterEdge.Async/ThrowHelper.cs diff --git a/src/CenterEdge.Async/AsyncHelper.cs b/src/CenterEdge.Async/AsyncHelper.cs index cf22c40..8b1e3dc 100644 --- a/src/CenterEdge.Async/AsyncHelper.cs +++ b/src/CenterEdge.Async/AsyncHelper.cs @@ -58,6 +58,16 @@ public static void RunSync(Func task) /// public static void RunSync(Func task, TState state) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(task)); + return; // unreachable, but helps static analysis + } +#endif + var oldContext = SynchronizationContext.Current; if (IsDeadlockSafe(oldContext)) @@ -101,6 +111,16 @@ public static void RunSync(Func task, TState state) [OverloadResolutionPriority(-1)] public static void RunSync(Func task) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(task)); + return; // unreachable, but helps static analysis + } +#endif + RunSync(static state => state.Invoke(), task); } @@ -116,6 +136,16 @@ public static void RunSync(Func task) [OverloadResolutionPriority(-1)] public static void RunSync(Func task, TState state) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(task)); + return; // unreachable, but helps static analysis + } +#endif + var oldContext = SynchronizationContext.Current; if (IsDeadlockSafe(oldContext)) @@ -161,6 +191,16 @@ public static void RunSync(Func task, TState state) /// public static T RunSync(Func> task) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(task)); + return default!; // unreachable, but helps static analysis + } +#endif + return RunSync(static state => state.Invoke(), task); } @@ -176,6 +216,16 @@ public static T RunSync(Func> task) /// public static T RunSync(Func> task, TState state) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(task)); + return default!; // unreachable, but helps static analysis + } +#endif + var oldContext = SynchronizationContext.Current; if (IsDeadlockSafe(oldContext)) @@ -219,6 +269,16 @@ public static T RunSync(Func> task, TState state) [OverloadResolutionPriority(-1)] public static T RunSync(Func> task) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(task)); + return default!; // unreachable, but helps static analysis + } +#endif + return RunSync(static state => state.Invoke(), task); } @@ -235,6 +295,16 @@ public static T RunSync(Func> task) [OverloadResolutionPriority(-1)] public static T RunSync(Func> task, TState state) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(task); +#else + if (task is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(task)); + return default!; // unreachable, but helps static analysis + } +#endif + var oldContext = SynchronizationContext.Current; if (IsDeadlockSafe(oldContext)) diff --git a/src/CenterEdge.Async/CenterEdge.Async.csproj b/src/CenterEdge.Async/CenterEdge.Async.csproj index e3a91e3..f6b5849 100644 --- a/src/CenterEdge.Async/CenterEdge.Async.csproj +++ b/src/CenterEdge.Async/CenterEdge.Async.csproj @@ -29,6 +29,10 @@ + + + + diff --git a/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs b/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs index e362d03..2224b4b 100644 --- a/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs +++ b/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs @@ -20,6 +20,15 @@ public override void Send(SendOrPostCallback d, object? state) public override void Post(SendOrPostCallback d, object? state) { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(d); +#else + if (state is null) + { + ThrowHelper.ThrowArgumentNullException(nameof(d)); + } +#endif + try { _items.Add(new WorkItem(d, state)); diff --git a/src/CenterEdge.Async/ThrowHelper.cs b/src/CenterEdge.Async/ThrowHelper.cs new file mode 100644 index 0000000..b794b17 --- /dev/null +++ b/src/CenterEdge.Async/ThrowHelper.cs @@ -0,0 +1,9 @@ +using System; + +namespace CenterEdge.Async; + +internal class ThrowHelper +{ + public static void ThrowArgumentNullException(string paramName) => + throw new ArgumentNullException(paramName); +} From eedb62f5ce7ad3e9239738c967764733848807d3 Mon Sep 17 00:00:00 2001 From: Brant Burnett Date: Mon, 29 Sep 2025 11:25:00 -0400 Subject: [PATCH 2/3] Update src/CenterEdge.Async/ExclusiveSynchronizationContext.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brant Burnett --- src/CenterEdge.Async/ExclusiveSynchronizationContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs b/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs index 2224b4b..1be77a0 100644 --- a/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs +++ b/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs @@ -23,7 +23,7 @@ public override void Post(SendOrPostCallback d, object? state) #if NET6_0_OR_GREATER ArgumentNullException.ThrowIfNull(d); #else - if (state is null) + if (d is null) { ThrowHelper.ThrowArgumentNullException(nameof(d)); } From e37b695c6f90ff493bcadbd95c5203a77eace6b2 Mon Sep 17 00:00:00 2001 From: Brant Burnett Date: Mon, 29 Sep 2025 11:28:42 -0400 Subject: [PATCH 3/3] Missed spot for nullable type analysis --- src/CenterEdge.Async/ExclusiveSynchronizationContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs b/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs index 1be77a0..3497cc9 100644 --- a/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs +++ b/src/CenterEdge.Async/ExclusiveSynchronizationContext.cs @@ -26,6 +26,7 @@ public override void Post(SendOrPostCallback d, object? state) if (d is null) { ThrowHelper.ThrowArgumentNullException(nameof(d)); + return; // unreachable, but helps static analysis } #endif