Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Implementation]: Use TimeSpan everywhere we use an int for seconds, milliseconds, and timeouts (Group 1/3) #64860

Merged
merged 75 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
377d39c
Add GC (CoreCLR) `TimeSpan` overloads
deeprobin Feb 5, 2022
4d5b632
Add `CancellationToken` overload for `Wait`
deeprobin Feb 5, 2022
3477de9
Add `MatchTimeout` property to `RegularExpressionAttribute`
deeprobin Feb 5, 2022
ca4e786
Add tests for `RegularExpressionAttribute.MatchTimeout`
deeprobin Feb 5, 2022
98664c6
Add `TimeSpan` overloads for `WaitForInputIdle` and `WaitForExit`
deeprobin Feb 5, 2022
94412fd
Add `TimeSpan` overload for `Timer` constructor
deeprobin Feb 5, 2022
31b2210
Add `TimeSpan` overload for `ServiceBase.RequestAdditionalTime`
deeprobin Feb 5, 2022
a3d856b
Add `TimeSpan` overload for `NetworkStream.Close`
deeprobin Feb 5, 2022
77e959b
Add `TimeSpan` overloads for `Socket.Poll` and `Socket.Select`
deeprobin Feb 5, 2022
ea86b67
Add `TimeSpan` overload for `FileSystemWatcher.WaitForChanged`
deeprobin Feb 5, 2022
517b8e7
Add `TimeSpan` overloads for `NamedPipeClientStram.Connect` and `Name…
deeprobin Feb 5, 2022
47cb9f8
Add `TimeSpan` overloads for `Ping.Send` and `Ping.SendPingAsync`
deeprobin Feb 5, 2022
829d412
Add more tests for `Ping.Send` and `Ping.SendPingAsync`
deeprobin Feb 5, 2022
f413126
Add tests for `NamedPipeClientStream.Connect` and `NamedPipeClientStr…
deeprobin Feb 5, 2022
516041c
Add more `Task.Wait` tests
deeprobin Feb 5, 2022
74fb1d9
Add ref
deeprobin Feb 5, 2022
7deaa98
Refactor RunContinuationCancelTest_TimeSpan test
deeprobin Feb 10, 2022
6827e20
Remove redundant code duplication
deeprobin Feb 10, 2022
615b66e
Remove extra line
deeprobin Feb 10, 2022
6413304
Use meaningful timeSpan
deeprobin Feb 10, 2022
004914c
Use Timeout.InfiniteTimeSpan
deeprobin Feb 10, 2022
091c289
Inline method call
deeprobin Feb 10, 2022
70c4efa
Use checked `Close`
deeprobin Feb 10, 2022
eeeb61b
Use expression body style in System.Net.Ping
deeprobin Feb 10, 2022
7446821
Implement CancellationTokenHandling to `SendPingAsync`
deeprobin Feb 10, 2022
5fba0ab
Fix PingTest
deeprobin Feb 10, 2022
738b9e2
Use expression-body method style for `GC`
deeprobin Feb 10, 2022
e6d68f9
Use expression-body method style for `Process`
deeprobin Feb 10, 2022
4a35788
Use expression-body method style for `FileSystemWatcher`
deeprobin Feb 10, 2022
486a761
Use expression-body method style for `NamedPipeStream`
deeprobin Feb 10, 2022
0a0794f
Merge branch 'issue-14336' of https://github.com/deeprobin/runtime in…
deeprobin Feb 10, 2022
9ffe1ae
Fix `NamedPipeTest.Specific`
deeprobin Feb 10, 2022
7d4af1d
Use expression-body method style for `NetworkStream` and `Socket`
deeprobin Feb 10, 2022
27fa0ce
Fix syntax errors in `PingTest`
deeprobin Feb 10, 2022
abea06f
Add `GC` implementation for NativeAOT (CoreRT)
deeprobin Feb 10, 2022
340e5eb
Add Mono `GC` implementation
deeprobin Feb 11, 2022
0ff42de
Next try to fix `RemoteExecutor` tests
deeprobin Feb 11, 2022
e61acf0
Fix `RemoteExecutor` tests
deeprobin Feb 11, 2022
5279de8
Fix net48 tests
deeprobin Feb 11, 2022
aad5405
Move ref into only-core ref & add cond. compilation
deeprobin Feb 11, 2022
1529601
Move `Timer`-`TimeSpan` overload in the right assembly
deeprobin Feb 14, 2022
c3ea515
Update `Task.Wait` so that it verifies the `CancellationToken` before…
deeprobin Feb 14, 2022
53bed23
Use `ToTimeoutMilliseconds` instead of a checked cast to int
deeprobin Feb 14, 2022
80f10be
Remove redundant TimerConstructor test
deeprobin Feb 15, 2022
6805ad4
Add setter to `RegularExpressionAttribute.MatchTimeout`
deeprobin Feb 15, 2022
f5e4844
Use `ToTimeoutMilliseconds` in `WaitForInputIdle`
deeprobin Feb 15, 2022
cf68f4d
Merge branch 'main' into issue-14336
deeprobin Feb 21, 2022
ff9c0e6
Use legacy syntax instead of pattern matching
deeprobin Mar 10, 2022
3be4f23
Merge branch 'main' into issue-14336
deeprobin Mar 18, 2022
099aacb
Remove setter from `RegularExpressionAttribute.MatchTimeout`
deeprobin Mar 18, 2022
d659806
Centralize `GC` methods
deeprobin Mar 18, 2022
eef5d71
Apply suggestions
deeprobin Mar 18, 2022
b9aca93
Add doc comment to `Process.WaitForInputIdle`
deeprobin Mar 18, 2022
ca7edd2
Reduce test duplication to reduce opportunities to flake out
deeprobin Mar 18, 2022
3a47e9d
Reduce test duplication for NamedPipes
deeprobin Mar 18, 2022
a469722
Reduce PingTests
deeprobin Mar 18, 2022
6251c56
Re-order `System.Net.Sockets` ref assembly entry
deeprobin Mar 18, 2022
a888f6e
Fix time unit conversion in `NetworkStream` & `Socket`
deeprobin Mar 18, 2022
c6a6c05
Use new overload in `Task.Wait(TimeSpan)`
deeprobin Mar 18, 2022
cfba2b9
Remove redundant test
deeprobin Mar 18, 2022
75b8554
Fix ping tests
deeprobin Mar 18, 2022
03aa02d
Fix Pipe tests
deeprobin Mar 18, 2022
25484a4
Use simpler validation and remove redundant resx-resources
deeprobin Mar 19, 2022
4c06f93
Remove unintentional changes
deeprobin Mar 19, 2022
ea03c06
Use `timeout.Ticks / 10` instead of floating-point multiplication
deeprobin Mar 25, 2022
4d2e023
Style fix
deeprobin Mar 25, 2022
bba4bd3
Improve naming: `timeoutMillis` -> `timeoutMilliseconds`
deeprobin Mar 25, 2022
c774885
Add tests
deeprobin Mar 25, 2022
2f47067
Fix file ending
deeprobin Mar 25, 2022
0d0015f
Fix sol file
deeprobin Mar 25, 2022
77f921f
Inline `ToTimeoutMilliseconds`
deeprobin Mar 26, 2022
a97f7d3
Fix coding style
deeprobin Mar 26, 2022
7cdf76c
Improve test maintainability of `System.Net.Sockets`
deeprobin Mar 26, 2022
4a73007
Apply suggestions
deeprobin Mar 26, 2022
f56905d
Update src/libraries/System.ServiceProcess.ServiceController/tests/Se…
danmoseley Mar 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/GC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace System
{
Expand Down Expand Up @@ -423,6 +424,8 @@ public static GCNotificationStatus WaitForFullGCApproach(int millisecondsTimeout
return (GCNotificationStatus)_WaitForFullGCApproach(millisecondsTimeout);
}

public static GCNotificationStatus WaitForFullGCApproach(TimeSpan timeout) => WaitForFullGCApproach(WaitHandle.ToTimeoutMilliseconds(timeout));

public static GCNotificationStatus WaitForFullGCComplete()
{
return (GCNotificationStatus)_WaitForFullGCComplete(-1);
Expand All @@ -435,6 +438,8 @@ public static GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout
return (GCNotificationStatus)_WaitForFullGCComplete(millisecondsTimeout);
}

public static GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout) => WaitForFullGCComplete(WaitHandle.ToTimeoutMilliseconds(timeout));

private enum StartNoGCRegionStatus
{
Succeeded = 0,
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,14 @@ public static GCNotificationStatus WaitForFullGCApproach(int millisecondsTimeout
return (GCNotificationStatus)RuntimeImports.RhWaitForFullGCApproach(millisecondsTimeout);
}

/// <summary>
/// Returns, in a specified time-out period, the status of a registered notification for determining whether a full,
/// blocking garbage collection by the common language runtime is imminent.
/// </summary>
/// <param name="timeout">The timeout on waiting for a full collection</param>
/// <returns>The status of a registered full GC notification</returns>
public static GCNotificationStatus WaitForFullGCApproach(TimeSpan timeout) => WaitForFullGCApproach(WaitHandle.ToTimeoutMilliseconds(timeout));

/// <summary>
/// Returns the status of a registered notification about whether a blocking garbage collection
/// has completed. May wait indefinitely for a full collection.
Expand Down Expand Up @@ -243,6 +251,14 @@ public static GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout
return (GCNotificationStatus)RuntimeImports.RhWaitForFullGCComplete(millisecondsTimeout);
}

/// <summary>
/// Returns, in a specified time-out period, the status of a registered notification for determining whether a full,
/// blocking garbage collection by common language the runtime has completed.
/// </summary>
/// <param name="timeout">The timeout on waiting for a full collection</param>
/// <returns>The status of the registered garbage collection notification.</returns>
public static GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout) => WaitForFullGCComplete(WaitHandle.ToTimeoutMilliseconds(timeout));

/// <summary>
/// Cancels an outstanding full GC notification.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ public partial class RegularExpressionAttribute : System.ComponentModel.DataAnno
{
public RegularExpressionAttribute([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute(System.Diagnostics.CodeAnalysis.StringSyntaxAttribute.Regex)] string pattern) { }
public int MatchTimeoutInMilliseconds { get { throw null; } set { } }
public System.TimeSpan MatchTimeout { get { throw null; } set { } }
public string Pattern { get { throw null; } }
public override string FormatErrorMessage(string name) { throw null; }
public override bool IsValid(object? value) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,10 @@
<data name="Validator_Property_Value_Wrong_Type" xml:space="preserve">
<value>The value for property '{0}' must be of type '{1}'.</value>
</data>
<data name="ArgumentOutOfRange_NeedNonNegOrNegative1" xml:space="preserve">
<value>Number must be either non-negative and less than or equal to Int32.MaxValue or -1.</value>
</data>
<data name="ArgumentOutOfRange_LessEqualToIntegerMaxVal" xml:space="preserve">
<value>Argument must be less than or equal to 2^31 - 1 milliseconds.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ public RegularExpressionAttribute([StringSyntax(StringSyntaxAttribute.Regex)] st
/// </summary>
public int MatchTimeoutInMilliseconds { get; set; }

/// <summary>
/// Gets or sets the timeout to use when matching the regular expression pattern
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gets or sets

I don't see a setter. Seems like we should have one as it wouldn't make sense to only support getting as TimeSpan

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!       This one might not end up being that useful, since people generally don't ever manipulate an instance of this attribute.
+       public TimeSpan MatchTimeout { get; }

I think that is so wanted that there is no setter (I have to adapt the documentation of course)

@terrajobst Can you verify that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have adjusted this despite the fact that we are waiting for the answer from Immo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@terrajobst What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartonjs with your API review hat on should we add a setter (was not in approved shape) for symmetry with the int property?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's contention then the best answer is probably to bring it up as a separate item to API Review, so we can discuss it in the larger group (if we want to keep it in this PR, then a blocking issue so it floats to the top).

My initial thought was "no", remembering the guideline as "avoid inter-dependent properties"; but it turns out that's just my rephrasing of one nuance of the actual guideline:

DO allow properties to be set in any order, even if this results in a temporary invalid state of the object.

With the Guideline As Written, it wouldn't be bad, just that the last writer wins. So, it's probably goodness to have... but since the original proposal didn't have it, and the review discussion didn't add it... it's probably worth having a chat again.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deeprobin Let's leave it out for now to get this PR done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading again.... this is an attribute. The 99.99% use case for setting properties on attributes is via [UsingAnAttribute(..., Property = Value)]. Since there is no attribute-literal syntax for a TimeSpan, the setter doesn't have much value.

Get-only seems more appropriate to me in this context.

/// </summary>
public TimeSpan MatchTimeout
{
get => TimeSpan.FromMilliseconds(MatchTimeoutInMilliseconds);
set
{
long timeoutMilliseconds = (long)value.TotalMilliseconds;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to just do what is good enough for Task today:

long totalMilliseconds = (long)timeout.TotalMilliseconds;
if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
}

that will be half the strings, and less duplicated code.

if it's important to tell them what's wrong, you could include the value in the string. (Task doesn't bother)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the setter because we don't need it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, but you're still repeating this validation code. my suggestion is to replace it everywhere with just the 5 lines above, effectively.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then you don't need new strings (or just 1 if you choose to include the value, which is nice)

if (timeoutMilliseconds < -1)
{
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
}

if (timeoutMilliseconds > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal);
}

MatchTimeoutInMilliseconds = (int)timeoutMilliseconds;
}
}

/// <summary>
/// Gets the regular expression pattern to use
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<Compile Include="System\ComponentModel\DataAnnotations\PhoneAttributeTests.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\RangeAttributeTests.cs" Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'" />
<Compile Include="System\ComponentModel\DataAnnotations\RegularExpressionAttributeTests.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\RegularExpressionAttributeTests.Core.cs" Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'" />
<Compile Include="System\ComponentModel\DataAnnotations\RequiredAttributeTests.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\ScaffoldColumnAttributeTests.cs" />
<Compile Include="System\ComponentModel\DataAnnotations\Schema\ColumnAttributeTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel.DataAnnotations;
using Xunit;

namespace System.ComponentModel.Annotations.Tests.System.ComponentModel.DataAnnotations
{
public sealed partial class RegularExpressionAttributeTests
{
[Theory]
[InlineData(12345)]
[InlineData(-1)]
public static void MatchTimeout_Get_ReturnsExpected(int newValue)
{
var attribute = new RegularExpressionAttribute("SomePattern") { MatchTimeoutInMilliseconds = newValue };
Assert.Equal(TimeSpan.FromMilliseconds(newValue), attribute.MatchTimeout);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace System.ComponentModel.DataAnnotations.Tests
{
public class RegularExpressionAttributeTests : ValidationAttributeTestBase
public sealed partial class RegularExpressionAttributeTests : ValidationAttributeTestBase
{
protected override IEnumerable<TestCase> ValidValues()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2306,6 +2306,7 @@ public partial class Timer : System.ComponentModel.Component, System.ComponentMo
{
public Timer() { }
public Timer(double interval) { }
public Timer(System.TimeSpan interval) { }
[System.ComponentModel.DefaultValueAttribute(true)]
public bool AutoReset { get { throw null; } set { } }
[System.ComponentModel.DefaultValueAttribute(false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ public Timer(double interval) : this()
_interval = (int)roundedInterval;
}

/// <summary>
/// Initializes a new instance of the <see cref='Timer'/> class, setting the <see cref='Interval'/> property to the specified period.
/// </summary>
public Timer(TimeSpan interval) : this(interval.TotalMilliseconds)
{
}

/// <summary>
/// Gets or sets a value indicating whether the Timer raises the Tick event each time the specified
/// Interval has elapsed, when Enabled is set to true.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,10 @@
<data name="LogAlreadyExistsAsSource" xml:space="preserve">
<value>Log {0} has already been registered as a source on the local computer.</value>
</data>
<data name="ArgumentOutOfRange_NeedNonNegOrNegative1" xml:space="preserve">
<value>Number must be either non-negative and less than or equal to Int32.MaxValue or -1.</value>
</data>
<data name="ArgumentOutOfRange_LessEqualToIntegerMaxVal" xml:space="preserve">
<value>Argument must be less than or equal to 2^31 - 1 milliseconds.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ public void Refresh() { }
public override string ToString() { throw null; }
public void WaitForExit() { }
public bool WaitForExit(int milliseconds) { throw null; }
public bool WaitForExit(System.TimeSpan timeout) { throw null; }
public System.Threading.Tasks.Task WaitForExitAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public bool WaitForInputIdle() { throw null; }
public bool WaitForInputIdle(int milliseconds) { throw null; }
public bool WaitForInputIdle(System.TimeSpan timeout) { throw null; }
}
[System.ComponentModel.DesignerAttribute("System.Diagnostics.Design.ProcessModuleDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public partial class ProcessModule : System.ComponentModel.Component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@
<data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
<value>Non-negative number required.</value>
</data>
<data name="ArgumentOutOfRange_NeedNonNegOrNegative1" xml:space="preserve">
<value>Number must be either non-negative and less than or equal to Int32.MaxValue or -1.</value>
</data>
<data name="ArgumentOutOfRange_LessEqualToIntegerMaxVal" xml:space="preserve">
<value>Argument must be less than or equal to 2^31 - 1 milliseconds.</value>
</data>
<data name="CantStartAsUser" xml:space="preserve">
<value>The Process object must have the UseShellExecute property set to false in order to start a process as a user.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,8 @@ public bool WaitForInputIdle(int milliseconds)
return WaitForInputIdleCore(milliseconds);
}

public bool WaitForInputIdle(TimeSpan timeout) => WaitForInputIdle(ToTimeoutMilliseconds(timeout));

public ISynchronizeInvoke? SynchronizingObject { get; set; }

/// <devdoc>
Expand Down Expand Up @@ -1414,6 +1416,27 @@ public bool WaitForExit(int milliseconds)
return exited;
}

/// <summary>
/// Instructs the Process component to wait the specified number of milliseconds for
/// the associated process to exit.
/// </summary>
public bool WaitForExit(TimeSpan timeout) => WaitForExit(ToTimeoutMilliseconds(timeout));

private static int ToTimeoutMilliseconds(TimeSpan timeout)
{
long timeoutMilliseconds = (long)timeout.TotalMilliseconds;
if (timeoutMilliseconds < -1)
{
throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
}

if (timeoutMilliseconds > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal);
}
return (int)timeoutMilliseconds;
}

/// <summary>
/// Instructs the Process component to wait for the associated process to exit, or
/// for the <paramref name="cancellationToken"/> to be canceled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ protected void OnError(System.IO.ErrorEventArgs e) { }
protected void OnRenamed(System.IO.RenamedEventArgs e) { }
public System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType) { throw null; }
public System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType, int timeout) { throw null; }
public System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType, System.TimeSpan timeout) { throw null; }
}
public partial class InternalBufferOverflowException : System.SystemException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@
<data name="ArgumentOutOfRange_FileLengthTooBig" xml:space="preserve">
<value>Specified file length was too large for the file system.</value>
</data>
<data name="ArgumentOutOfRange_NeedNonNegOrNegative1" xml:space="preserve">
<value>Number must be either non-negative and less than or equal to Int32.MaxValue or -1.</value>
</data>
<data name="ArgumentOutOfRange_LessEqualToIntegerMaxVal" xml:space="preserve">
<value>Argument must be less than or equal to 2^31 - 1 milliseconds.</value>
</data>
<data name="IO_PathTooLong" xml:space="preserve">
<value>The specified file name or path is too long, or a component of the specified path is too long.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,24 @@ public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int ti
WaitForChangedResult.TimedOutResult;
}

public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout) =>
WaitForChanged(changeType, ToTimeoutMilliseconds(timeout));

private static int ToTimeoutMilliseconds(TimeSpan timeout)
{
long timeoutMilliseconds = (long)timeout.TotalMilliseconds;
if (timeoutMilliseconds < -1)
{
throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
}

if (timeoutMilliseconds > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_LessEqualToIntegerMaxVal);
}
return (int)timeoutMilliseconds;
}

/// <devdoc>
/// Stops and starts this object.
/// </devdoc>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,57 @@ public void NonZeroTimeout_NoActivity_TimesOut(WatcherChangeTypes changeType, bo
}
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void ZeroTimeout_TimesOut_TimeSpan(bool enabledBeforeWait)
{
TimeSpan timeout = TimeSpan.FromMilliseconds(0);

using var testDirectory = new TempDirectory(GetTestFilePath());
using var dir = new TempDirectory(Path.Combine(testDirectory.Path, GetTestFileName()));
using var fsw = new FileSystemWatcher(testDirectory.Path);

if (enabledBeforeWait) fsw.EnableRaisingEvents = true;
AssertTimedOut(fsw.WaitForChanged(WatcherChangeTypes.All, timeout));
Assert.Equal(enabledBeforeWait, fsw.EnableRaisingEvents);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void NonZeroTimeout_NoEvents_TimesOut_TimeSpan(bool enabledBeforeWait)
{
TimeSpan timeout = TimeSpan.FromMilliseconds(1);

using var testDirectory = new TempDirectory(GetTestFilePath());
using var dir = new TempDirectory(Path.Combine(testDirectory.Path, GetTestFileName()));
using var fsw = new FileSystemWatcher(testDirectory.Path);

if (enabledBeforeWait) fsw.EnableRaisingEvents = true;
AssertTimedOut(fsw.WaitForChanged(0, timeout));
Assert.Equal(enabledBeforeWait, fsw.EnableRaisingEvents);
}

[Theory]
[InlineData(WatcherChangeTypes.Deleted, false)]
[InlineData(WatcherChangeTypes.Created, true)]
[InlineData(WatcherChangeTypes.Changed, false)]
[InlineData(WatcherChangeTypes.Renamed, true)]
[InlineData(WatcherChangeTypes.All, true)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/58418", typeof(PlatformDetection), nameof(PlatformDetection.IsMacCatalyst), nameof(PlatformDetection.IsArm64Process))]
public void NonZeroTimeout_NoActivity_TimesOut_TimeSpan(WatcherChangeTypes changeType, bool enabledBeforeWait)
{
TimeSpan timeout = TimeSpan.FromMilliseconds(1);
using var testDirectory = new TempDirectory(GetTestFilePath());
using var dir = new TempDirectory(Path.Combine(testDirectory.Path, GetTestFileName()));
using var fsw = new FileSystemWatcher(testDirectory.Path);

if (enabledBeforeWait) fsw.EnableRaisingEvents = true;
AssertTimedOut(fsw.WaitForChanged(changeType, timeout));
Assert.Equal(enabledBeforeWait, fsw.EnableRaisingEvents);
}

[Theory]
[OuterLoop("This test has a longer than average timeout and may fail intermittently")]
[InlineData(WatcherChangeTypes.Created)]
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ public sealed partial class NamedPipeClientStream : System.IO.Pipes.PipeStream
protected internal override void CheckPipePropertyOperations() { }
public void Connect() { }
public void Connect(int timeout) { }
public void Connect(System.TimeSpan timeout) { }
public System.Threading.Tasks.Task ConnectAsync() { throw null; }
public System.Threading.Tasks.Task ConnectAsync(int timeout) { throw null; }
public System.Threading.Tasks.Task ConnectAsync(int timeout, System.Threading.CancellationToken cancellationToken) { throw null; }
public System.Threading.Tasks.Task ConnectAsync(System.TimeSpan timeout, System.Threading.CancellationToken cancellationToken) { throw null; }
public System.Threading.Tasks.Task ConnectAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
~NamedPipeClientStream() { }
}
Expand Down
6 changes: 6 additions & 0 deletions src/libraries/System.IO.Pipes/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@
<data name="ArgumentOutOfRange_NeedValidPipeAccessRights" xml:space="preserve">
<value>Invalid PipeAccessRights value.</value>
</data>
<data name="ArgumentOutOfRange_NeedNonNegOrNegative1" xml:space="preserve">
<value>Number must be either non-negative and less than or equal to Int32.MaxValue or -1.</value>
</data>
<data name="ArgumentOutOfRange_LessEqualToIntegerMaxVal" xml:space="preserve">
<value>Argument must be less than or equal to 2^31 - 1 milliseconds.</value>
</data>
<data name="Argument_NonContainerInvalidAnyFlag" xml:space="preserve">
<value>This flag may not be set on a pipe.</value>
</data>
Expand Down
Loading