From d0e6cc01b0044b5f712cc36a177fdff8c1aaebd8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:23:48 +0000 Subject: [PATCH 1/3] Initial plan From 6646346d0e1efe9540af365b130fabbed7542941 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:32:34 +0000 Subject: [PATCH 2/3] Make DelayHelper a public API in the Iot.Device namespace --- src/devices/Common/ClassVisibility.cs | 6 +-- .../Common/System/Device/DelayHelper.cs | 14 +++++- src/devices/Common/tests/DelayHelperTest.cs | 45 +++++++++++++++++++ src/devices/SoftPwm/SoftwarePwmChannel.cs | 1 + 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/devices/Common/tests/DelayHelperTest.cs diff --git a/src/devices/Common/ClassVisibility.cs b/src/devices/Common/ClassVisibility.cs index 4f22c20709..1b2cd226f0 100644 --- a/src/devices/Common/ClassVisibility.cs +++ b/src/devices/Common/ClassVisibility.cs @@ -24,7 +24,7 @@ public partial class NumberHelper } } -namespace System.Device +namespace Iot.Device { public partial class DelayHelper { @@ -55,9 +55,9 @@ internal partial class NumberHelper } } -namespace System.Device +namespace Iot.Device { - internal partial class DelayHelper + public partial class DelayHelper { } } diff --git a/src/devices/Common/System/Device/DelayHelper.cs b/src/devices/Common/System/Device/DelayHelper.cs index e2aafabc2b..243d4ea6c1 100644 --- a/src/devices/Common/System/Device/DelayHelper.cs +++ b/src/devices/Common/System/Device/DelayHelper.cs @@ -1,13 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Diagnostics; using System.Threading; -namespace System.Device +namespace Iot.Device { /// - /// Helpers for short waits. + /// Helpers for short, high precision waits. Prefer these over when a device + /// binding needs to honor sub-millisecond timing requirements from a datasheet. /// static partial class DelayHelper { @@ -79,7 +81,15 @@ public static void DelayMicroseconds(int microseconds, bool allowThreadYield) /// True to allow yielding the thread. If this is set to false, on single-proc systems /// this will prevent all other code from running. /// + /// + /// This overload is intended for internal use only. For millisecond scale waits prefer + /// , which lets the operating system schedule other work. + /// +#if BUILDING_IOT_DEVICE_BINDINGS + internal static void DelayMilliseconds(int milliseconds, bool allowThreadYield) +#else public static void DelayMilliseconds(int milliseconds, bool allowThreadYield) +#endif { /* We have this as a separate method for now to make calling code clearer * and to allow us to add additional logic to the millisecond wait in the diff --git a/src/devices/Common/tests/DelayHelperTest.cs b/src/devices/Common/tests/DelayHelperTest.cs new file mode 100644 index 0000000000..30fd600629 --- /dev/null +++ b/src/devices/Common/tests/DelayHelperTest.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using Xunit; + +namespace Iot.Device.Common.Tests +{ + public class DelayHelperTest + { + [Theory] + [InlineData(true)] + [InlineData(false)] + public void DelayWaitsAtLeastTheRequestedTime(bool allowThreadYield) + { + TimeSpan requested = TimeSpan.FromMilliseconds(20); + + Stopwatch stopwatch = Stopwatch.StartNew(); + DelayHelper.Delay(requested, allowThreadYield); + stopwatch.Stop(); + + // The helper guarantees to wait for *at least* the requested time. A small tolerance is + // allowed to account for the resolution of the underlying timer. + Assert.True( + stopwatch.Elapsed >= requested - TimeSpan.FromMilliseconds(2), + $"Expected to wait at least {requested.TotalMilliseconds}ms but only waited {stopwatch.Elapsed.TotalMilliseconds}ms."); + } + + [Fact] + public void DelayMicrosecondsWaitsAtLeastTheRequestedTime() + { + const int microseconds = 5000; // 5ms + TimeSpan requested = TimeSpan.FromMilliseconds(5); + + Stopwatch stopwatch = Stopwatch.StartNew(); + DelayHelper.DelayMicroseconds(microseconds, allowThreadYield: true); + stopwatch.Stop(); + + Assert.True( + stopwatch.Elapsed >= requested - TimeSpan.FromMilliseconds(1), + $"Expected to wait at least {requested.TotalMilliseconds}ms but only waited {stopwatch.Elapsed.TotalMilliseconds}ms."); + } + } +} diff --git a/src/devices/SoftPwm/SoftwarePwmChannel.cs b/src/devices/SoftPwm/SoftwarePwmChannel.cs index ca33393460..2434b3d7e6 100644 --- a/src/devices/SoftPwm/SoftwarePwmChannel.cs +++ b/src/devices/SoftPwm/SoftwarePwmChannel.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Globalization; using System.Threading; +using Iot.Device; namespace System.Device.Pwm.Drivers { From b4ff5d05f1b81e2d6ffb6a48f39aaac0b32044ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:36:58 +0000 Subject: [PATCH 3/3] Use a consistent tolerance in DelayHelper timing tests --- src/devices/Common/tests/DelayHelperTest.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/devices/Common/tests/DelayHelperTest.cs b/src/devices/Common/tests/DelayHelperTest.cs index 30fd600629..75b916ec9c 100644 --- a/src/devices/Common/tests/DelayHelperTest.cs +++ b/src/devices/Common/tests/DelayHelperTest.cs @@ -9,6 +9,11 @@ namespace Iot.Device.Common.Tests { public class DelayHelperTest { + // Timing tests only verify the guaranteed lower bound (the helper waits for *at least* the + // requested time). A small, consistent tolerance absorbs the resolution of the underlying + // timer and the conversion between Stopwatch ticks and TimeSpan ticks. + private static readonly TimeSpan Tolerance = TimeSpan.FromMilliseconds(1); + [Theory] [InlineData(true)] [InlineData(false)] @@ -20,10 +25,8 @@ public void DelayWaitsAtLeastTheRequestedTime(bool allowThreadYield) DelayHelper.Delay(requested, allowThreadYield); stopwatch.Stop(); - // The helper guarantees to wait for *at least* the requested time. A small tolerance is - // allowed to account for the resolution of the underlying timer. Assert.True( - stopwatch.Elapsed >= requested - TimeSpan.FromMilliseconds(2), + stopwatch.Elapsed >= requested - Tolerance, $"Expected to wait at least {requested.TotalMilliseconds}ms but only waited {stopwatch.Elapsed.TotalMilliseconds}ms."); } @@ -38,7 +41,7 @@ public void DelayMicrosecondsWaitsAtLeastTheRequestedTime() stopwatch.Stop(); Assert.True( - stopwatch.Elapsed >= requested - TimeSpan.FromMilliseconds(1), + stopwatch.Elapsed >= requested - Tolerance, $"Expected to wait at least {requested.TotalMilliseconds}ms but only waited {stopwatch.Elapsed.TotalMilliseconds}ms."); } }