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.");
}
}