diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index adf2908c581ab9..036936d4f58f9d 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -143,7 +143,7 @@ - + @@ -333,9 +333,9 @@ + Outputs="@(EventingSourceFile)" + DependsOnTargets="FindPython" + BeforeTargets="BeforeCompile"> diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs similarity index 100% rename from src/coreclr/System.Private.CoreLib/src/System/GC.cs rename to src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 523b6b8c3a814d..2b562e55213dbc 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -169,7 +169,7 @@ - + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.CoreRT.cs similarity index 99% rename from src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs rename to src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.CoreRT.cs index c9578dfee08036..783f75f41ef6a4 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.CoreRT.cs @@ -58,7 +58,7 @@ internal enum EndNoGCRegionStatus AllocationExceeded = 3 } - public static class GC + public static partial class GC { public static int GetGeneration(object obj) { diff --git a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs index cc7dfb6120ef18..c6a9b98a8eba03 100644 --- a/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs +++ b/src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs @@ -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; } } public string Pattern { get { throw null; } } public override string FormatErrorMessage(string name) { throw null; } public override bool IsValid(object? value) { throw null; } diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs index cf2ec8a7697c20..b21d843d060f7d 100644 --- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs +++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs @@ -31,6 +31,11 @@ public RegularExpressionAttribute([StringSyntax(StringSyntaxAttribute.Regex)] st /// public int MatchTimeoutInMilliseconds { get; set; } + /// + /// Gets the timeout to use when matching the regular expression pattern + /// + public TimeSpan MatchTimeout => TimeSpan.FromMilliseconds(MatchTimeoutInMilliseconds); + /// /// Gets the regular expression pattern to use /// diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj b/src/libraries/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj index d4e20cee535409..ea79a73bf74a66 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj +++ b/src/libraries/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj @@ -28,6 +28,7 @@ + diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.Core.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.Core.cs new file mode 100644 index 00000000000000..78bb2e5e028fd6 --- /dev/null +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.Core.cs @@ -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); + } + } +} diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.cs index 78ac307027f371..45a9da9712f262 100644 --- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.cs +++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.cs @@ -7,7 +7,7 @@ namespace System.ComponentModel.DataAnnotations.Tests { - public class RegularExpressionAttributeTests : ValidationAttributeTestBase + public sealed partial class RegularExpressionAttributeTests : ValidationAttributeTestBase { protected override IEnumerable ValidValues() { diff --git a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs index f1a8f6f698f15c..04d75256cb4384 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs @@ -2338,6 +2338,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)] diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs index 0dc77a54ccda65..826257de2e7cf0 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs @@ -59,6 +59,13 @@ public Timer(double interval) : this() _interval = (int)roundedInterval; } + /// + /// Initializes a new instance of the class, setting the property to the specified period. + /// + public Timer(TimeSpan interval) : this(interval.TotalMilliseconds) + { + } + /// /// 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. diff --git a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs index d6975bd7274153..5dcecba7702110 100644 --- a/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs +++ b/src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs @@ -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 diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs index 2aeee088c509e9..7e4c4953f4d057 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs @@ -826,6 +826,39 @@ public bool WaitForInputIdle(int milliseconds) return WaitForInputIdleCore(milliseconds); } + /// + /// Causes the component to wait the specified for the associated process to enter an idle state. + /// This overload applies only to processes with a user interface and, therefore, a message loop. + /// + /// The amount of time, in milliseconds, to wait for the associated process to become idle. + /// if the associated process has reached an idle state; otherwise, . + /// + /// The process does not have a graphical interface. + /// + /// -or- + /// + /// An unknown error occurred. The process failed to enter an idle state. + /// + /// -or- + /// + /// The process has already exited. + /// + /// -or- + /// + /// No process is associated with this object. + /// + /// + /// Use to force the processing of your application + /// to wait until the message loop has returned to the idle state. + /// When a process with a user interface is executing, its message loop executes every time + /// a Windows message is sent to the process by the operating system. + /// The process then returns to the message loop. A process is said to be in an idle state + /// when it is waiting for messages inside of a message loop. + /// This state is useful, for example, when your application needs to wait for a starting process + /// to finish creating its main window before the application communicates with that window. + /// + public bool WaitForInputIdle(TimeSpan timeout) => WaitForInputIdle(ToTimeoutMilliseconds(timeout)); + public ISynchronizeInvoke? SynchronizingObject { get; set; } /// @@ -1414,6 +1447,22 @@ public bool WaitForExit(int milliseconds) return exited; } + /// + /// Instructs the Process component to wait the specified number of milliseconds for + /// the associated process to exit. + /// + public bool WaitForExit(TimeSpan timeout) => WaitForExit(ToTimeoutMilliseconds(timeout)); + + private static int ToTimeoutMilliseconds(TimeSpan timeout) + { + long totalMilliseconds = (long)timeout.TotalMilliseconds; + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + return (int)totalMilliseconds; + } + /// /// Instructs the Process component to wait for the associated process to exit, or /// for the to be canceled. diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index 441036b8380fe2..90dc6a8b4ba8f6 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -74,6 +74,15 @@ private void AssertNonZeroAllZeroDarwin(long value) } } + [Theory] + [InlineData(-2)] + [InlineData((long)int.MaxValue + 1)] + public void TestWaitForExitValidation(long milliseconds) + { + CreateDefaultProcess(); + Assert.Throws("timeout", () => _process.WaitForExit(TimeSpan.FromMilliseconds(milliseconds))); + } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [PlatformSpecific(TestPlatforms.Windows)] // Expected behavior varies on Windows and Unix public void TestBasePriorityOnWindows() diff --git a/src/libraries/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs b/src/libraries/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs index 1f926bff17f315..d640d89cfe3888 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs @@ -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 { diff --git a/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs b/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs index fd9c2ce4fa6baf..654bd3ab856c8f 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs @@ -618,6 +618,19 @@ 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 totalMilliseconds = (long)timeout.TotalMilliseconds; + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + return (int)totalMilliseconds; + } + /// /// Stops and starts this object. /// diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.WaitForChanged.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.WaitForChanged.cs index cce5f54469f2dd..f2558953d26086 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.WaitForChanged.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.WaitForChanged.cs @@ -76,50 +76,73 @@ public static void WaitForChangedResult_TimedOut_Roundtrip() } [Theory] - [InlineData(false)] - [InlineData(true)] - public void ZeroTimeout_TimesOut(bool enabledBeforeWait) + [InlineData(-2)] + [InlineData((long)int.MaxValue + 1)] + public void TimeSpan_ArgumentValidation(long milliseconds) + { + TimeSpan timeout = TimeSpan.FromMilliseconds(milliseconds); + using var testDirectory = new TempDirectory(GetTestFilePath()); + using var _ = new TempDirectory(Path.Combine(testDirectory.Path, GetTestFileName())); + using var fsw = new FileSystemWatcher(testDirectory.Path); + + Assert.Throws("timeout", () => fsw.WaitForChanged(WatcherChangeTypes.All, timeout)); + } + + [Theory] + [InlineData(false, true)] + [InlineData(true, false)] + public void ZeroTimeout_TimesOut(bool enabledBeforeWait, bool useTimeSpan) { 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, 0)); + + const int timeoutMilliseconds = 0; + AssertTimedOut(useTimeSpan + ? fsw.WaitForChanged(WatcherChangeTypes.All, TimeSpan.FromMilliseconds(timeoutMilliseconds)) + : fsw.WaitForChanged(WatcherChangeTypes.All, timeoutMilliseconds)); Assert.Equal(enabledBeforeWait, fsw.EnableRaisingEvents); } } [Theory] - [InlineData(false)] - [InlineData(true)] - public void NonZeroTimeout_NoEvents_TimesOut(bool enabledBeforeWait) + [InlineData(false, false)] + [InlineData(true, true)] + public void NonZeroTimeout_NoEvents_TimesOut(bool enabledBeforeWait, bool useTimeSpan) { 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, 1)); + const int timeoutMilliseconds = 1; + AssertTimedOut(useTimeSpan + ? fsw.WaitForChanged(0, TimeSpan.FromMilliseconds(timeoutMilliseconds)) + : fsw.WaitForChanged(0, timeoutMilliseconds)); 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)] + [InlineData(WatcherChangeTypes.Deleted, false, true)] + [InlineData(WatcherChangeTypes.Created, true, false)] + [InlineData(WatcherChangeTypes.Changed, false, true)] + [InlineData(WatcherChangeTypes.Renamed, true, false)] + [InlineData(WatcherChangeTypes.All, true, true)] [ActiveIssue("https://github.com/dotnet/runtime/issues/58418", typeof(PlatformDetection), nameof(PlatformDetection.IsMacCatalyst), nameof(PlatformDetection.IsArm64Process))] - public void NonZeroTimeout_NoActivity_TimesOut(WatcherChangeTypes changeType, bool enabledBeforeWait) + public void NonZeroTimeout_NoActivity_TimesOut(WatcherChangeTypes changeType, bool enabledBeforeWait, bool useTimeSpan) { 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, 1)); + const int timeoutMilliseconds = 1; + AssertTimedOut(useTimeSpan + ? fsw.WaitForChanged(changeType, TimeSpan.FromMilliseconds(timeoutMilliseconds)) + : fsw.WaitForChanged(changeType, timeoutMilliseconds)); Assert.Equal(enabledBeforeWait, fsw.EnableRaisingEvents); } } diff --git a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs index 3966988ecad2d1..a7432dbc5a2177 100644 --- a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs +++ b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs @@ -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() { } } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs index 8a7a9213a76ffa..1667631bda0f67 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs @@ -125,6 +125,8 @@ public void Connect(int timeout) ConnectInternal(timeout, CancellationToken.None, Environment.TickCount); } + public void Connect(TimeSpan timeout) => Connect(ToTimeoutMilliseconds(timeout)); + private void ConnectInternal(int timeout, CancellationToken cancellationToken, int startTime) { // This is the main connection loop. It will loop until the timeout expires. @@ -193,6 +195,19 @@ public Task ConnectAsync(int timeout, CancellationToken cancellationToken) return Task.Run(() => ConnectInternal(timeout, cancellationToken, startTime), cancellationToken); } + public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken = default) => + ConnectAsync(ToTimeoutMilliseconds(timeout), cancellationToken); + + private static int ToTimeoutMilliseconds(TimeSpan timeout) + { + long totalMilliseconds = (long)timeout.TotalMilliseconds; + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + return (int)totalMilliseconds; + } + // override because named pipe clients can't get/set properties when waiting to connect // or broken protected internal override void CheckPipePropertyOperations() diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CurrentUserOnly.Windows.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CurrentUserOnly.Windows.cs index 439454502655d8..e4f9a045759a7c 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CurrentUserOnly.Windows.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CurrentUserOnly.Windows.cs @@ -162,12 +162,14 @@ public void Connection_UnderDifferentUsers_BehavesAsExpected( } [OuterLoop] - [ConditionalFact(nameof(IsAdminOnSupportedWindowsVersions))] - public void Allow_Connection_UnderDifferentUsers_ForClientReading() + [ConditionalTheory(nameof(IsAdminOnSupportedWindowsVersions))] + [InlineData(false)] + [InlineData(true)] + public void Allow_Connection_UnderDifferentUsers_ForClientReading(bool useTimeSpan) { string name = PipeStreamConformanceTests.GetUniquePipeName(); using (var server = new NamedPipeServerStream( - name, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) + name, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) { Task serverTask = server.WaitForConnectionAsync(CancellationToken.None); @@ -175,7 +177,14 @@ public void Allow_Connection_UnderDifferentUsers_ForClientReading() { using (var client = new NamedPipeClientStream(".", name, PipeDirection.In)) { - client.Connect(10_000); + if (useTimeSpan) + { + client.Connect(TimeSpan.FromMilliseconds(10_000)); + } + else + { + client.Connect(10_000); + } } }); diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs index 220be9d65ca8a8..64744e5a4e4fbb 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs @@ -23,6 +23,10 @@ public void InvalidConnectTimeout_Throws_ArgumentOutOfRangeException() { AssertExtensions.Throws("timeout", () => client.Connect(-111)); AssertExtensions.Throws("timeout", () => { client.ConnectAsync(-111); }); + AssertExtensions.Throws("timeout", () => client.Connect(TimeSpan.FromMilliseconds(-2))); + AssertExtensions.Throws("timeout", () => { client.ConnectAsync(TimeSpan.FromMilliseconds(-2), default); }); + AssertExtensions.Throws("timeout", () => client.Connect(TimeSpan.FromMilliseconds((long)int.MaxValue + 1))); + AssertExtensions.Throws("timeout", () => { client.ConnectAsync(TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default); }); } } @@ -32,9 +36,12 @@ public async Task ConnectToNonExistentServer_Throws_TimeoutException() using (NamedPipeClientStream client = new NamedPipeClientStream(".", "notthere")) { var ctx = new CancellationTokenSource(); - Assert.Throws(() => client.Connect(60)); // 60 to be over internal 50 interval - await Assert.ThrowsAsync(() => client.ConnectAsync(50)); - await Assert.ThrowsAsync(() => client.ConnectAsync(60, ctx.Token)); // testing Token overload; ctx is not canceled in this test + Assert.Throws(() => + client.Connect(TimeSpan.FromMilliseconds(60))); // 60 to be over internal 50 interval + await Assert.ThrowsAsync(() => client.ConnectAsync(TimeSpan.FromMilliseconds(50), default)); + await Assert.ThrowsAsync(() => + client.ConnectAsync(TimeSpan.FromMilliseconds(60), + ctx.Token)); // testing Token overload; ctx is not canceled in this test } } @@ -611,7 +618,8 @@ public async Task ClientConnectAsync_Throws_Timeout_When_Pipe_Not_Found(Cancella string pipeName = PipeStreamConformanceTests.GetUniquePipeName(); using (NamedPipeClientStream client = new NamedPipeClientStream(pipeName)) { - Task waitingClient = client.ConnectAsync(92, cancellationToken); + TimeSpan timeout = TimeSpan.FromMilliseconds(92); + Task waitingClient = client.ConnectAsync(timeout, cancellationToken); await Assert.ThrowsAsync(() => { return waitingClient; }); } } @@ -626,16 +634,18 @@ public void ClientConnect_Throws_Timeout_When_Pipe_Busy() using (NamedPipeClientStream firstClient = new NamedPipeClientStream(pipeName)) using (NamedPipeClientStream secondClient = new NamedPipeClientStream(pipeName)) { - const int timeout = 10_000; + var ctx = new CancellationTokenSource(); + TimeSpan timeout = TimeSpan.FromMilliseconds(10_000); Task[] clientAndServerTasks = new[] { - firstClient.ConnectAsync(timeout), + firstClient.ConnectAsync(timeout, ctx.Token), Task.Run(() => server.WaitForConnection()) }; Assert.True(Task.WaitAll(clientAndServerTasks, timeout)); - Assert.Throws(() => secondClient.Connect(93)); + TimeSpan connectionTimeout = TimeSpan.FromMilliseconds(93); + Assert.Throws(() => secondClient.Connect(connectionTimeout)); } } @@ -650,16 +660,17 @@ public async Task ClientConnectAsync_With_Cancellation_Throws_Timeout_When_Pipe_ using (NamedPipeClientStream firstClient = new NamedPipeClientStream(pipeName)) using (NamedPipeClientStream secondClient = new NamedPipeClientStream(pipeName)) { - const int timeout = 10_000; + TimeSpan timeout = TimeSpan.FromMilliseconds(10_000); Task[] clientAndServerTasks = new[] { - firstClient.ConnectAsync(timeout), + firstClient.ConnectAsync(timeout, cancellationToken), Task.Run(() => server.WaitForConnection()) }; Assert.True(Task.WaitAll(clientAndServerTasks, timeout)); - Task waitingClient = secondClient.ConnectAsync(94, cancellationToken); + TimeSpan connectionTimeout = TimeSpan.FromMilliseconds(94); + Task waitingClient = secondClient.ConnectAsync(connectionTimeout, cancellationToken); await Assert.ThrowsAsync(() => { return waitingClient; }); } } diff --git a/src/libraries/System.Net.Ping/ref/System.Net.Ping.cs b/src/libraries/System.Net.Ping/ref/System.Net.Ping.cs index 82dcd06b86b9b7..71d2e314241d63 100644 --- a/src/libraries/System.Net.Ping/ref/System.Net.Ping.cs +++ b/src/libraries/System.Net.Ping/ref/System.Net.Ping.cs @@ -47,6 +47,8 @@ protected void OnPingCompleted(System.Net.NetworkInformation.PingCompletedEventA public System.Net.NetworkInformation.PingReply Send(string hostNameOrAddress, int timeout) { throw null; } public System.Net.NetworkInformation.PingReply Send(string hostNameOrAddress, int timeout, byte[] buffer) { throw null; } public System.Net.NetworkInformation.PingReply Send(string hostNameOrAddress, int timeout, byte[] buffer, System.Net.NetworkInformation.PingOptions? options) { throw null; } + public System.Net.NetworkInformation.PingReply Send(System.Net.IPAddress address, System.TimeSpan timeout, byte[]? buffer, System.Net.NetworkInformation.PingOptions? options) { throw null; } + public System.Net.NetworkInformation.PingReply Send(string hostNameOrAddress, System.TimeSpan timeout, byte[]? buffer, System.Net.NetworkInformation.PingOptions? options) { throw null; } public void SendAsync(System.Net.IPAddress address, int timeout, byte[] buffer, System.Net.NetworkInformation.PingOptions? options, object? userToken) { } public void SendAsync(System.Net.IPAddress address, int timeout, byte[] buffer, object? userToken) { } public void SendAsync(System.Net.IPAddress address, int timeout, object? userToken) { } @@ -64,6 +66,8 @@ public void SendAsyncCancel() { } public System.Threading.Tasks.Task SendPingAsync(string hostNameOrAddress, int timeout) { throw null; } public System.Threading.Tasks.Task SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer) { throw null; } public System.Threading.Tasks.Task SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer, System.Net.NetworkInformation.PingOptions? options) { throw null; } + public System.Threading.Tasks.Task SendPingAsync(System.Net.IPAddress address, System.TimeSpan timeout, byte[]? buffer, System.Net.NetworkInformation.PingOptions? options, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task SendPingAsync(string hostNameOrAddress, System.TimeSpan timeout, byte[]? buffer, System.Net.NetworkInformation.PingOptions? options, System.Threading.CancellationToken cancellationToken) { throw null; } } public partial class PingCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs index 125f793c98c962..106501c913642c 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs @@ -231,6 +231,12 @@ public PingReply Send(IPAddress address, int timeout, byte[] buffer, PingOptions } } + public PingReply Send(IPAddress address, TimeSpan timeout, byte[]? buffer = null, PingOptions? options = null) => + Send(address, ToTimeoutMilliseconds(timeout), buffer ?? DefaultSendBuffer, options); + + public PingReply Send(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, + PingOptions? options = null) => Send(hostNameOrAddress, ToTimeoutMilliseconds(timeout), buffer ?? DefaultSendBuffer, options); + public void SendAsync(string hostNameOrAddress, object? userToken) { SendAsync(hostNameOrAddress, DefaultTimeout, DefaultSendBuffer, userToken); @@ -318,6 +324,24 @@ public Task SendPingAsync(IPAddress address, int timeout, byte[] buff return SendPingAsyncInternal(address, timeout, buffer, options); } + public Task SendPingAsync(IPAddress address, TimeSpan timeout, byte[]? buffer = null, + PingOptions? options = null, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + Task task = SendPingAsync(address, ToTimeoutMilliseconds(timeout), buffer ?? DefaultSendBuffer, options); + + return task.WaitAsync(cancellationToken); + } + + public Task SendPingAsync(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null, + PingOptions? options = null, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + Task task = SendPingAsync(hostNameOrAddress, ToTimeoutMilliseconds(timeout), buffer ?? DefaultSendBuffer, options); + + return task.WaitAsync(cancellationToken); + } + private async Task SendPingAsyncInternal(IPAddress address, int timeout, byte[] buffer, PingOptions? options) { // Need to snapshot the address here, so we're sure that it's not changed between now @@ -357,6 +381,16 @@ public Task SendPingAsync(string hostNameOrAddress, int timeout, byte return GetAddressAndSendAsync(hostNameOrAddress, timeout, buffer, options); } + private static int ToTimeoutMilliseconds(TimeSpan timeout) + { + long totalMilliseconds = (long)timeout.TotalMilliseconds; + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + return (int)totalMilliseconds; + } + public void SendAsyncCancel() { lock (_lockObject) diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs index c506643f9c8f56..1e1d1568943b63 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs @@ -3,6 +3,7 @@ using Microsoft.DotNet.XUnitExtensions; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Net.Sockets; using System.Net.Test.Common; @@ -47,7 +48,7 @@ public PingTest(ITestOutputHelper output) private void PingResultValidator(PingReply pingReply, IPAddress[] localIpAddresses) => PingResultValidator(pingReply, localIpAddresses, null); - private static void PingResultValidator(PingReply pingReply, IPAddress[] localIpAddresses, ITestOutputHelper output) + private static void PingResultValidator(PingReply pingReply, IPAddress[] localIpAddresses, ITestOutputHelper? output) { Assert.Equal(IPStatus.Success, pingReply.Status); if (localIpAddresses.Any(addr => pingReply.Address.Equals(addr))) @@ -105,10 +106,18 @@ public async Task SendPingAsync_InvalidArgs() // Negative timeout AssertExtensions.Throws("timeout", () => { p.SendPingAsync(localIpAddress, -1); }); AssertExtensions.Throws("timeout", () => { p.SendPingAsync(TestSettings.LocalHost, -1); }); + AssertExtensions.Throws("timeout", () => { p.SendPingAsync(localIpAddress, TimeSpan.FromMilliseconds(-1), default, default, default); }); + AssertExtensions.Throws("timeout", () => { p.SendPingAsync(TestSettings.LocalHost, TimeSpan.FromMilliseconds(-1), default, default, default); }); + AssertExtensions.Throws("timeout", () => { p.SendPingAsync(localIpAddress, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default, default); }); + AssertExtensions.Throws("timeout", () => { p.SendPingAsync(TestSettings.LocalHost, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default, default); }); AssertExtensions.Throws("timeout", () => { p.SendAsync(localIpAddress, -1, null); }); AssertExtensions.Throws("timeout", () => { p.SendAsync(TestSettings.LocalHost, -1, null); }); AssertExtensions.Throws("timeout", () => { p.Send(localIpAddress, -1); }); AssertExtensions.Throws("timeout", () => { p.Send(TestSettings.LocalHost, -1); }); + AssertExtensions.Throws("timeout", () => { p.Send(localIpAddress, TimeSpan.FromMilliseconds(-1), default, default); }); + AssertExtensions.Throws("timeout", () => { p.Send(TestSettings.LocalHost, TimeSpan.FromMilliseconds(-1), default, default); }); + AssertExtensions.Throws("timeout", () => { p.Send(localIpAddress, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default); }); + AssertExtensions.Throws("timeout", () => { p.Send(TestSettings.LocalHost, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default); }); // Null byte[] AssertExtensions.Throws("buffer", () => { p.SendPingAsync(localIpAddress, 0, null); }); @@ -814,17 +823,15 @@ public void SendPing_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, str public void SendPingAsync_LocaleEnvVarsMustBeIgnored(AddressFamily addressFamily, string envVar_LANG, string envVar_LC_MESSAGES, string envVar_LC_ALL) { IPAddress localIpAddress = TestSettings.GetLocalIPAddress(addressFamily); - if (localIpAddress == null) - { - // No local address for given address family. - return; - } - - var remoteInvokeStartInfo = new ProcessStartInfo(); - remoteInvokeStartInfo.EnvironmentVariables["LANG"] = envVar_LANG; - remoteInvokeStartInfo.EnvironmentVariables["LC_MESSAGES"] = envVar_LC_MESSAGES; - remoteInvokeStartInfo.EnvironmentVariables["LC_ALL"] = envVar_LC_ALL; + var remoteInvokeStartInfo = new ProcessStartInfo { + EnvironmentVariables = + { + ["LANG"] = envVar_LANG, + ["LC_MESSAGES"] = envVar_LC_MESSAGES, + ["LC_ALL"] = envVar_LC_ALL + } + }; RemoteExecutor.Invoke(async address => { diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index e813cca044ee04..3eec205b412966 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -136,6 +136,7 @@ public NetworkStream(System.Net.Sockets.Socket socket, System.IO.FileAccess acce public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } public override System.IAsyncResult BeginWrite(byte[] buffer, int offset, int count, System.AsyncCallback? callback, object? state) { throw null; } public void Close(int timeout) { } + public void Close(System.TimeSpan timeout) { } protected override void Dispose(bool disposing) { } public override int EndRead(System.IAsyncResult asyncResult) { throw null; } public override void EndWrite(System.IAsyncResult asyncResult) { } @@ -371,6 +372,7 @@ public void GetSocketOption(System.Net.Sockets.SocketOptionLevel optionLevel, Sy public void Listen() { } public void Listen(int backlog) { } public bool Poll(int microSeconds, System.Net.Sockets.SelectMode mode) { throw null; } + public bool Poll(System.TimeSpan timeout, System.Net.Sockets.SelectMode mode) { throw null; } public int Receive(byte[] buffer) { throw null; } public int Receive(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags) { throw null; } public int Receive(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, out System.Net.Sockets.SocketError errorCode) { throw null; } @@ -408,6 +410,7 @@ public void Listen(int backlog) { } public System.Threading.Tasks.ValueTask ReceiveMessageFromAsync(System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; } public bool ReceiveMessageFromAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; } public static void Select(System.Collections.IList? checkRead, System.Collections.IList? checkWrite, System.Collections.IList? checkError, int microSeconds) { } + public static void Select(System.Collections.IList? checkRead, System.Collections.IList? checkWrite, System.Collections.IList? checkError, System.TimeSpan timeout) { } public int Send(byte[] buffer) { throw null; } public int Send(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags) { throw null; } public int Send(byte[] buffer, int offset, int size, System.Net.Sockets.SocketFlags socketFlags, out System.Net.Sockets.SocketError errorCode) { throw null; } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs index 6e57472a199f08..299091bf28f3dd 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs @@ -337,6 +337,18 @@ public void Close(int timeout) Dispose(); } + public void Close(TimeSpan timeout) => Close(ToTimeoutSeconds(timeout)); + + private static int ToTimeoutSeconds(TimeSpan timeout) + { + long totalSeconds = (long)timeout.TotalSeconds; + if (totalSeconds < -1 || totalSeconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + return (int)totalSeconds; + } + protected override void Dispose(bool disposing) { if (Interlocked.Exchange(ref _disposed, 1) != 0) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index ea137810083a8e..d0478278b83277 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -2138,6 +2138,9 @@ public bool Poll(int microSeconds, SelectMode mode) return status; } + public bool Poll(TimeSpan timeout, SelectMode mode) => + Poll(ToTimeoutMicroseconds(timeout), mode); + // Determines the status of a socket. public static void Select(IList? checkRead, IList? checkWrite, IList? checkError, int microSeconds) { @@ -2171,6 +2174,18 @@ public static void Select(IList? checkRead, IList? checkWrite, IList? checkError } } + public static void Select(IList? checkRead, IList? checkWrite, IList? checkError, TimeSpan timeout) => Select(checkRead, checkWrite, checkError, ToTimeoutMicroseconds(timeout)); + + private static int ToTimeoutMicroseconds(TimeSpan timeout) + { + long totalMicroseconds = timeout.Ticks / 10; + if (totalMicroseconds < -1 || totalMicroseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(timeout)); + } + return (int)totalMicroseconds; + } + public IAsyncResult BeginConnect(EndPoint remoteEP, AsyncCallback? callback, object? state) => TaskToApm.Begin(ConnectAsync(remoteEP), callback, state); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs index 41fe74b0c4f757..31da90800351ab 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs @@ -355,6 +355,22 @@ public void Select_NullOrEmptyLists_Throws_ArgumentNull() Assert.Throws(() => Socket.Select(emptyList, emptyList, emptyList, -1)); } + [Fact] + public void Select_NullOrEmptyLists_Throws_ArgumentNull_TimeSpan() + { + TimeSpan nonInfinity = TimeSpan.FromMilliseconds(1); + var emptyList = new List(); + + Assert.Throws(() => Socket.Select(null, null, null, nonInfinity)); + Assert.Throws(() => Socket.Select(emptyList, null, null, nonInfinity)); + Assert.Throws(() => Socket.Select(null, emptyList, null, nonInfinity)); + Assert.Throws(() => Socket.Select(emptyList, emptyList, null, nonInfinity)); + Assert.Throws(() => Socket.Select(null, null, emptyList, nonInfinity)); + Assert.Throws(() => Socket.Select(emptyList, null, emptyList, nonInfinity)); + Assert.Throws(() => Socket.Select(null, emptyList, emptyList, nonInfinity)); + Assert.Throws(() => Socket.Select(emptyList, emptyList, emptyList, nonInfinity)); + } + [Fact] public void Select_LargeList_Throws_ArgumentOutOfRange() { @@ -365,6 +381,22 @@ public void Select_LargeList_Throws_ArgumentOutOfRange() Assert.Throws(() => Socket.Select(null, null, largeList, -1)); } + [Fact] + public void Select_LargeList_Throws_ArgumentOutOfRange_TimeSpan() + { + var largeList = new LargeList(); + + TimeSpan infinity = Timeout.InfiniteTimeSpan; + Assert.Throws(() => Socket.Select(largeList, null, null, infinity)); + Assert.Throws(() => Socket.Select(null, largeList, null, infinity)); + Assert.Throws(() => Socket.Select(null, null, largeList, infinity)); + + TimeSpan negative = TimeSpan.FromMilliseconds(-1); + Assert.Throws(() => Socket.Select(largeList, null, null, negative)); + Assert.Throws(() => Socket.Select(null, largeList, null, negative)); + Assert.Throws(() => Socket.Select(null, null, largeList, negative)); + } + [Fact] public void AcceptAsync_NullAsyncEventArgs_Throws_ArgumentNull() { diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs index 39506cee57fdaf..954e70fa7fe980 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs @@ -341,8 +341,10 @@ public async Task ConnectHostNameAndPort_CancelDuringConnect_Throws() } } - [Fact] - public async Task FailedConnect_ConnectedReturnsFalse() + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task FailedConnect_ConnectedReturnsFalse(bool useTimeSpan) { using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); @@ -354,7 +356,14 @@ public async Task FailedConnect_ConnectedReturnsFalse() Assert.Equal(SocketError.WouldBlock, se.SocketErrorCode); // Give the non-blocking connect some time to complete. - socket.Poll(5_000_000 /* microSeconds */, SelectMode.SelectWrite); + if (useTimeSpan) + { + socket.Poll(TimeSpan.FromMilliseconds(5000), SelectMode.SelectWrite); + } + else + { + socket.Poll(5_000_000 /* microSeconds */, SelectMode.SelectWrite); + } } Assert.False(socket.Connected); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 3a846e7828eab4..8889b675d14f9c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -309,6 +309,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/GC.cs b/src/libraries/System.Private.CoreLib/src/System/GC.cs new file mode 100644 index 00000000000000..de53aa1296c5a3 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/GC.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace System +{ + public static partial class GC + { + /// + /// 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. + /// + /// The timeout on waiting for a full GC approach + /// The status of a registered full GC notification + public static GCNotificationStatus WaitForFullGCApproach(TimeSpan timeout) + => WaitForFullGCApproach(WaitHandle.ToTimeoutMilliseconds(timeout)); + + /// + /// Returns the status of a registered notification about whether a blocking garbage collection + /// has completed. May wait indefinitely for a full collection. + /// + /// The timeout on waiting for a full collection + /// The status of a registered full GC notification + public static GCNotificationStatus WaitForFullGCComplete(TimeSpan timeout) + => WaitForFullGCComplete(WaitHandle.ToTimeoutMilliseconds(timeout)); + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index 421af0e5034b0e..f3aa1a57fd48aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -2637,15 +2637,41 @@ public void Wait() /// infinite time-out -or- timeout is greater than /// . /// - public bool Wait(TimeSpan timeout) + public bool Wait(TimeSpan timeout) => Wait(timeout, default); + + /// + /// Waits for the to complete execution. + /// + /// The time to wait, or to wait indefinitely + /// + /// A to observe while waiting for the task to complete. + /// + /// + /// true if the completed execution within the allotted time; otherwise, false. + /// + /// + /// The was canceled -or- an exception was thrown during the execution of the . + /// + /// + /// is a negative number other than -1 milliseconds, which represents an + /// infinite time-out -or- timeout is greater than + /// . + /// + /// + /// The was canceled. + /// + public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + long totalMilliseconds = (long)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout); } - return Wait((int)totalMilliseconds, default); + return Wait((int)totalMilliseconds, cancellationToken); } /// diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index bf155afc8500ff..73e14973650f63 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2437,8 +2437,10 @@ public static void SuppressFinalize(object obj) { } public static bool TryStartNoGCRegion(long totalSize, long lohSize, bool disallowFullBlockingGC) { throw null; } public static System.GCNotificationStatus WaitForFullGCApproach() { throw null; } public static System.GCNotificationStatus WaitForFullGCApproach(int millisecondsTimeout) { throw null; } + public static System.GCNotificationStatus WaitForFullGCApproach(System.TimeSpan timeout) { throw null; } public static System.GCNotificationStatus WaitForFullGCComplete() { throw null; } public static System.GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout) { throw null; } + public static System.GCNotificationStatus WaitForFullGCComplete(System.TimeSpan timeout) { throw null; } public static void WaitForPendingFinalizers() { } } public enum GCCollectionMode @@ -13356,6 +13358,7 @@ public void Wait() { } public bool Wait(int millisecondsTimeout, System.Threading.CancellationToken cancellationToken) { throw null; } public void Wait(System.Threading.CancellationToken cancellationToken) { } public bool Wait(System.TimeSpan timeout) { throw null; } + public bool Wait(System.TimeSpan timeout, System.Threading.CancellationToken cancellationToken) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public static void WaitAll(params System.Threading.Tasks.Task[] tasks) { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] diff --git a/src/libraries/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.netcoreapp.cs b/src/libraries/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.netcoreapp.cs index 1298a7c23d6909..42be0a6c2c64f1 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.netcoreapp.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.netcoreapp.cs @@ -6,6 +6,10 @@ namespace System.ServiceProcess { + public partial class ServiceBase : System.ComponentModel.Component + { + public void RequestAdditionalTime(System.TimeSpan time) { } + } public partial class ServiceController : System.ComponentModel.Component { public void Stop(bool stopDependentServices) { } diff --git a/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs b/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs index c8dcaa55b8936c..74e95338d51a73 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs @@ -72,6 +72,26 @@ public unsafe void RequestAdditionalTime(int milliseconds) } } +#if NETCOREAPP + /// + /// When this method is called from OnStart, OnStop, OnPause or OnContinue, + /// the specified wait hint is passed to the + /// Service Control Manager to avoid having the service marked as not responding. + /// + /// The requested additional time + public void RequestAdditionalTime(TimeSpan time) => RequestAdditionalTime(ToIntMilliseconds(time)); + + private static int ToIntMilliseconds(TimeSpan time) + { + long totalMilliseconds = (long)time.TotalMilliseconds; + if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(time)); + } + return (int)totalMilliseconds; + } +#endif + /// /// Indicates whether to report Start, Stop, Pause, and Continue commands in the event. /// diff --git a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs index 247d600b86c768..67954fda143349 100644 --- a/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs +++ b/src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs @@ -72,6 +72,18 @@ private void Cleanup() } } +#if NETCOREAPP + [Theory] + [InlineData(-2)] + [InlineData((long)int.MaxValue + 1)] + public void RequestAdditionalTime_Throws_ArgumentOutOfRangeException(long milliseconds) + { + TimeSpan time = TimeSpan.FromMilliseconds(milliseconds); + using var serviceBase = new ServiceBase(); + Assert.Throws("time", () => serviceBase.RequestAdditionalTime(time)); + } +#endif + [ConditionalFact(nameof(IsProcessElevated))] public void TestOnStartThenStop() { diff --git a/src/libraries/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj b/src/libraries/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj index 2253b5e87e79d9..ca9d7b875714f1 100644 --- a/src/libraries/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj +++ b/src/libraries/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj @@ -7,6 +7,7 @@ + diff --git a/src/libraries/System.Threading.Tasks/tests/Task/TaskArgumentValidationTests.cs b/src/libraries/System.Threading.Tasks/tests/Task/TaskArgumentValidationTests.cs new file mode 100644 index 00000000000000..99892193107546 --- /dev/null +++ b/src/libraries/System.Threading.Tasks/tests/Task/TaskArgumentValidationTests.cs @@ -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 Xunit; + +namespace System.Threading.Tasks.Tests +{ + public sealed class TaskArgumentValidationTests + { + [Theory] + [InlineData(-2)] + [InlineData((long)int.MaxValue + 1)] + public void Task_Wait_ArgumentOutOfRange(long milliseconds) + { + TimeSpan timeout = TimeSpan.FromMilliseconds(milliseconds); + Task task = Task.Run(static () => {}); + Assert.Throws("timeout", () => task.Wait(timeout)); + } + } +} diff --git a/src/libraries/System.Threading.Tasks/tests/Task/TaskContinueWithTests.cs b/src/libraries/System.Threading.Tasks/tests/Task/TaskContinueWithTests.cs index 35f7917eba9b50..11d2e326bcc6f8 100644 --- a/src/libraries/System.Threading.Tasks/tests/Task/TaskContinueWithTests.cs +++ b/src/libraries/System.Threading.Tasks/tests/Task/TaskContinueWithTests.cs @@ -256,8 +256,10 @@ public static void RunContinueWithParamsTest_IllegalArgs() } // Test what happens when you cancel a task in the middle of a continuation chain. - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - public static void RunContinuationCancelTest() + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] + [InlineData(false)] + [InlineData(true)] + public static void RunContinuationCancelTest(bool useTimeSpan) { bool t1Ran = false; bool t3Ran = false; @@ -266,9 +268,9 @@ public static void RunContinuationCancelTest() CancellationTokenSource ctsForT2 = new CancellationTokenSource(); Task t2 = t1.ContinueWith((ContinuedTask) => - { - Assert.True(false, string.Format("RunContinuationCancelTest: > Failed! t2 should not have run.")); - }, ctsForT2.Token); + { + Assert.True(false, string.Format("RunContinuationCancelTest: > Failed! t2 should not have run.")); + }, ctsForT2.Token); Task t3 = t2.ContinueWith((ContinuedTask) => { @@ -281,8 +283,17 @@ public static void RunContinuationCancelTest() // Start the first task in the chain. Should hold off from kicking off (canceled) t2. t1.Start(); - t1.Wait(5000); // should be more than enough time for either of these - t3.Wait(5000); + if (useTimeSpan) + { + TimeSpan timeout = TimeSpan.FromMilliseconds(5000); + t1.Wait(timeout); + t3.Wait(timeout); + } + else + { + t1.Wait(5000); // should be more than enough time for either of these + t3.Wait(5000); + } if (!t1Ran) {