[API Implementation]: Use TimeSpan everywhere we use an int for seconds, milliseconds...
authorRobin Lindner <robin@deeprobin.de>
Mon, 28 Mar 2022 14:14:09 +0000 (16:14 +0200)
committerGitHub <noreply@github.com>
Mon, 28 Mar 2022 14:14:09 +0000 (08:14 -0600)
39 files changed:
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs [moved from src/coreclr/System.Private.CoreLib/src/System/GC.cs with 100% similarity]
src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.CoreRT.cs [moved from src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.cs with 99% similarity]
src/libraries/System.ComponentModel.Annotations/ref/System.ComponentModel.Annotations.cs
src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RegularExpressionAttribute.cs
src/libraries/System.ComponentModel.Annotations/tests/System.ComponentModel.Annotations.Tests.csproj
src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.Core.cs [new file with mode: 0644]
src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/RegularExpressionAttributeTests.cs
src/libraries/System.ComponentModel.TypeConverter/ref/System.ComponentModel.TypeConverter.cs
src/libraries/System.ComponentModel.TypeConverter/src/System/Timers/Timer.cs
src/libraries/System.Diagnostics.Process/ref/System.Diagnostics.Process.cs
src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs
src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs
src/libraries/System.IO.FileSystem.Watcher/ref/System.IO.FileSystem.Watcher.cs
src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.cs
src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.WaitForChanged.cs
src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs
src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs
src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CurrentUserOnly.Windows.cs
src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs
src/libraries/System.Net.Ping/ref/System.Net.Ping.cs
src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs
src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs
src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/NetworkStream.cs
src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs
src/libraries/System.Net.Sockets/tests/FunctionalTests/Connect.cs
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/GC.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.ServiceProcess.ServiceController/ref/System.ServiceProcess.ServiceController.netcoreapp.cs
src/libraries/System.ServiceProcess.ServiceController/src/System/ServiceProcess/ServiceBase.cs
src/libraries/System.ServiceProcess.ServiceController/tests/ServiceBaseTests.cs
src/libraries/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj
src/libraries/System.Threading.Tasks/tests/Task/TaskArgumentValidationTests.cs [new file with mode: 0644]
src/libraries/System.Threading.Tasks/tests/Task/TaskContinueWithTests.cs

index b84e52b..c9c3bca 100644 (file)
     <Compile Include="$(BclSourcesRoot)\System\Enum.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Environment.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Exception.CoreCLR.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\GC.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\GC.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\IO\FileLoadException.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\IO\FileNotFoundException.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Math.CoreCLR.cs" />
 
   <Target Name="GenerateEventingFiles"
           Inputs="@(EventingGenerationScript);@(EventManifestFile)"
-          Outputs="@(EventingSourceFile)"
-          DependsOnTargets="FindPython"
-          BeforeTargets="BeforeCompile">
+                 Outputs="@(EventingSourceFile)"
+                 DependsOnTargets="FindPython"
+                 BeforeTargets="BeforeCompile">
 
     <Error Condition="'$(PYTHON)' == ''" Text="Unable to locate Python. NativeRuntimeEventSource.CoreCLR.cs cannot be generated without Python installed on the machine. Either install Python in your path or point to it with the PYTHON environment variable." />
     <PropertyGroup>
index a4d2dd6..ce2c001 100644 (file)
     <Compile Include="System\Diagnostics\StackTrace.CoreRT.cs" />
     <Compile Include="System\Enum.CoreRT.cs" />
     <Compile Include="System\Environment.CoreRT.cs" />
-    <Compile Include="System\GC.cs" />
+    <Compile Include="System\GC.CoreRT.cs" />
     <Compile Include="System\Helpers.cs" />
     <Compile Include="System\Math.CoreRT.cs" />
     <Compile Include="System\MathF.CoreRT.cs" />
@@ -58,7 +58,7 @@ namespace System
         AllocationExceeded = 3
     }
 
-    public static class GC
+    public static partial class GC
     {
         public static int GetGeneration(object obj)
         {
index cc7dfb6..c6a9b98 100644 (file)
@@ -237,6 +237,7 @@ namespace System.ComponentModel.DataAnnotations
     {
         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; }
index cf2ec8a..b21d843 100644 (file)
@@ -32,6 +32,11 @@ namespace System.ComponentModel.DataAnnotations
         public int MatchTimeoutInMilliseconds { get; set; }
 
         /// <summary>
+        /// Gets the timeout to use when matching the regular expression pattern
+        /// </summary>
+        public TimeSpan MatchTimeout => TimeSpan.FromMilliseconds(MatchTimeoutInMilliseconds);
+
+        /// <summary>
         ///     Gets the regular expression pattern to use
         /// </summary>
         public string Pattern { get; }
index d4e20ce..ea79a73 100644 (file)
@@ -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" />
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 (file)
index 0000000..78bb2e5
--- /dev/null
@@ -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);
+        }
+    }
+}
index 78ac307..45a9da9 100644 (file)
@@ -7,7 +7,7 @@ using Xunit;
 
 namespace System.ComponentModel.DataAnnotations.Tests
 {
-    public class RegularExpressionAttributeTests : ValidationAttributeTestBase
+    public sealed partial class RegularExpressionAttributeTests : ValidationAttributeTestBase
     {
         protected override IEnumerable<TestCase> ValidValues()
         {
index f1a8f6f..04d7525 100644 (file)
@@ -2338,6 +2338,7 @@ namespace System.Timers
     {
         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)]
index 0dc77a5..826257d 100644 (file)
@@ -60,6 +60,13 @@ namespace System.Timers
         }
 
         /// <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.
         /// </summary>
index d6975bd..5dcecba 100644 (file)
@@ -168,9 +168,11 @@ namespace System.Diagnostics
         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
index 2aeee08..7e4c495 100644 (file)
@@ -826,6 +826,39 @@ namespace System.Diagnostics
             return WaitForInputIdleCore(milliseconds);
         }
 
+        /// <summary>
+        /// Causes the <see cref="Process"/> component to wait the specified <paramref name="timeout"/> for the associated process to enter an idle state.
+        /// This overload applies only to processes with a user interface and, therefore, a message loop.
+        /// </summary>
+        /// <param name="timeout">The amount of time, in milliseconds, to wait for the associated process to become idle.</param>
+        /// <returns><see langword="true"/> if the associated process has reached an idle state; otherwise, <see langword="false"/>.</returns>
+        /// <exception cref="InvalidOperationException">
+        /// 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 <see cref="Process"/> object.
+        /// </exception>
+        /// <remarks>
+        /// Use <see cref="WaitForInputIdle(TimeSpan)"/> 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.
+        /// </remarks>
+        public bool WaitForInputIdle(TimeSpan timeout) => WaitForInputIdle(ToTimeoutMilliseconds(timeout));
+
         public ISynchronizeInvoke? SynchronizingObject { get; set; }
 
         /// <devdoc>
@@ -1415,6 +1448,22 @@ namespace System.Diagnostics
         }
 
         /// <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 totalMilliseconds = (long)timeout.TotalMilliseconds;
+            if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue)
+            {
+                throw new ArgumentOutOfRangeException(nameof(timeout));
+            }
+            return (int)totalMilliseconds;
+        }
+
+        /// <summary>
         /// Instructs the Process component to wait for the associated process to exit, or
         /// for the <paramref name="cancellationToken"/> to be canceled.
         /// </summary>
index 441036b..90dc6a8 100644 (file)
@@ -74,6 +74,15 @@ namespace System.Diagnostics.Tests
             }
         }
 
+        [Theory]
+        [InlineData(-2)]
+        [InlineData((long)int.MaxValue + 1)]
+        public void TestWaitForExitValidation(long milliseconds)
+        {
+            CreateDefaultProcess();
+            Assert.Throws<ArgumentOutOfRangeException>("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()
index 1f926bf..d640d89 100644 (file)
@@ -50,6 +50,7 @@ namespace System.IO
         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
     {
index 81cd480..b7cea76 100644 (file)
@@ -618,6 +618,19 @@ namespace System.IO
                 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;
+        }
+
         /// <devdoc>
         ///     Stops and starts this object.
         /// </devdoc>
index cce5f54..f255895 100644 (file)
@@ -76,50 +76,73 @@ namespace System.IO.Tests
         }
 
         [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<ArgumentOutOfRangeException>("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);
             }
         }
index 3966988..a7432db 100644 (file)
@@ -54,9 +54,11 @@ namespace System.IO.Pipes
         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() { }
     }
index 8a7a921..1667631 100644 (file)
@@ -125,6 +125,8 @@ namespace System.IO.Pipes
             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 @@ namespace System.IO.Pipes
             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()
index 4394545..e4f9a04 100644 (file)
@@ -162,12 +162,14 @@ namespace System.IO.Pipes.Tests
         }
 
         [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 @@ namespace System.IO.Pipes.Tests
                 {
                     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);
+                        }
                     }
                 });
 
index 220be9d..64744e5 100644 (file)
@@ -23,6 +23,10 @@ namespace System.IO.Pipes.Tests
             {
                 AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => client.Connect(-111));
                 AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { client.ConnectAsync(-111); });
+                AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => client.Connect(TimeSpan.FromMilliseconds(-2)));
+                AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { client.ConnectAsync(TimeSpan.FromMilliseconds(-2), default); });
+                AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => client.Connect(TimeSpan.FromMilliseconds((long)int.MaxValue + 1)));
+                AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { client.ConnectAsync(TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default); });
             }
         }
 
@@ -32,9 +36,12 @@ namespace System.IO.Pipes.Tests
             using (NamedPipeClientStream client = new NamedPipeClientStream(".", "notthere"))
             {
                 var ctx = new CancellationTokenSource();
-                Assert.Throws<TimeoutException>(() => client.Connect(60));  // 60 to be over internal 50 interval
-                await Assert.ThrowsAsync<TimeoutException>(() => client.ConnectAsync(50));
-                await Assert.ThrowsAsync<TimeoutException>(() => client.ConnectAsync(60, ctx.Token)); // testing Token overload; ctx is not canceled in this test
+                Assert.Throws<TimeoutException>(() =>
+                    client.Connect(TimeSpan.FromMilliseconds(60))); // 60 to be over internal 50 interval
+                await Assert.ThrowsAsync<TimeoutException>(() => client.ConnectAsync(TimeSpan.FromMilliseconds(50), default));
+                await Assert.ThrowsAsync<TimeoutException>(() =>
+                    client.ConnectAsync(TimeSpan.FromMilliseconds(60),
+                        ctx.Token)); // testing Token overload; ctx is not canceled in this test
             }
         }
 
@@ -611,7 +618,8 @@ namespace System.IO.Pipes.Tests
             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<TimeoutException>(() => { return waitingClient; });
             }
         }
@@ -626,16 +634,18 @@ namespace System.IO.Pipes.Tests
             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<TimeoutException>(() => secondClient.Connect(93));
+                TimeSpan connectionTimeout = TimeSpan.FromMilliseconds(93);
+                Assert.Throws<TimeoutException>(() => secondClient.Connect(connectionTimeout));
             }
         }
 
@@ -650,16 +660,17 @@ namespace System.IO.Pipes.Tests
             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<TimeoutException>(() => { return waitingClient; });
             }
         }
index 82dcd06..71d2e31 100644 (file)
@@ -47,6 +47,8 @@ namespace System.Net.NetworkInformation
         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 @@ namespace System.Net.NetworkInformation
         public System.Threading.Tasks.Task<System.Net.NetworkInformation.PingReply> SendPingAsync(string hostNameOrAddress, int timeout) { throw null; }
         public System.Threading.Tasks.Task<System.Net.NetworkInformation.PingReply> SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer) { throw null; }
         public System.Threading.Tasks.Task<System.Net.NetworkInformation.PingReply> SendPingAsync(string hostNameOrAddress, int timeout, byte[] buffer, System.Net.NetworkInformation.PingOptions? options) { throw null; }
+        public System.Threading.Tasks.Task<System.Net.NetworkInformation.PingReply> 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<System.Net.NetworkInformation.PingReply> 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
     {
index 5c6447a..da83882 100644 (file)
@@ -231,6 +231,12 @@ namespace System.Net.NetworkInformation
             }
         }
 
+        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 @@ namespace System.Net.NetworkInformation
             return SendPingAsyncInternal(address, timeout, buffer, options);
         }
 
+        public Task<PingReply> SendPingAsync(IPAddress address, TimeSpan timeout, byte[]? buffer = null,
+            PingOptions? options = null, CancellationToken cancellationToken = default)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+            Task<PingReply> task = SendPingAsync(address, ToTimeoutMilliseconds(timeout), buffer ?? DefaultSendBuffer, options);
+
+            return task.WaitAsync(cancellationToken);
+        }
+
+        public Task<PingReply> SendPingAsync(string hostNameOrAddress, TimeSpan timeout, byte[]? buffer = null,
+            PingOptions? options = null, CancellationToken cancellationToken = default)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+            Task<PingReply> task = SendPingAsync(hostNameOrAddress, ToTimeoutMilliseconds(timeout), buffer ?? DefaultSendBuffer, options);
+
+            return task.WaitAsync(cancellationToken);
+        }
+
         private async Task<PingReply> 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 @@ namespace System.Net.NetworkInformation
             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)
index c506643..1e1d156 100644 (file)
@@ -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 @@ namespace System.Net.NetworkInformation.Tests
 
         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 @@ namespace System.Net.NetworkInformation.Tests
             // Negative timeout
             AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendPingAsync(localIpAddress, -1); });
             AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendPingAsync(TestSettings.LocalHost, -1); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendPingAsync(localIpAddress, TimeSpan.FromMilliseconds(-1), default, default, default); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendPingAsync(TestSettings.LocalHost, TimeSpan.FromMilliseconds(-1), default, default, default); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendPingAsync(localIpAddress, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default, default); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendPingAsync(TestSettings.LocalHost, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default, default); });
             AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendAsync(localIpAddress, -1, null); });
             AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.SendAsync(TestSettings.LocalHost, -1, null); });
             AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.Send(localIpAddress, -1); });
             AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.Send(TestSettings.LocalHost, -1); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.Send(localIpAddress, TimeSpan.FromMilliseconds(-1), default, default); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.Send(TestSettings.LocalHost, TimeSpan.FromMilliseconds(-1), default, default); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.Send(localIpAddress, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default); });
+            AssertExtensions.Throws<ArgumentOutOfRangeException>("timeout", () => { p.Send(TestSettings.LocalHost, TimeSpan.FromMilliseconds((long)int.MaxValue + 1), default, default); });
 
             // Null byte[]
             AssertExtensions.Throws<ArgumentNullException>("buffer", () => { p.SendPingAsync(localIpAddress, 0, null); });
@@ -814,17 +823,15 @@ namespace System.Net.NetworkInformation.Tests
         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 =>
             {
index e813cca..3eec205 100644 (file)
@@ -136,6 +136,7 @@ namespace System.Net.Sockets
         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 @@ namespace System.Net.Sockets
         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 @@ namespace System.Net.Sockets
         public System.Threading.Tasks.ValueTask<System.Net.Sockets.SocketReceiveMessageFromResult> ReceiveMessageFromAsync(System.Memory<byte> 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; }
index 6e57472..299091b 100644 (file)
@@ -337,6 +337,18 @@ namespace System.Net.Sockets
             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)
index d03327f..592f4ef 100644 (file)
@@ -2138,6 +2138,9 @@ namespace System.Net.Sockets
             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 @@ namespace System.Net.Sockets
             }
         }
 
+        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);
 
index 41fe74b..31da908 100644 (file)
@@ -356,6 +356,22 @@ namespace System.Net.Sockets.Tests
         }
 
         [Fact]
+        public void Select_NullOrEmptyLists_Throws_ArgumentNull_TimeSpan()
+        {
+            TimeSpan nonInfinity = TimeSpan.FromMilliseconds(1);
+            var emptyList = new List<Socket>();
+
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(null, null, null, nonInfinity));
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(emptyList, null, null, nonInfinity));
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(null, emptyList, null, nonInfinity));
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(emptyList, emptyList, null, nonInfinity));
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(null, null, emptyList, nonInfinity));
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(emptyList, null, emptyList, nonInfinity));
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(null, emptyList, emptyList, nonInfinity));
+            Assert.Throws<ArgumentNullException>(() => Socket.Select(emptyList, emptyList, emptyList, nonInfinity));
+        }
+
+        [Fact]
         public void Select_LargeList_Throws_ArgumentOutOfRange()
         {
             var largeList = new LargeList();
@@ -366,6 +382,22 @@ namespace System.Net.Sockets.Tests
         }
 
         [Fact]
+        public void Select_LargeList_Throws_ArgumentOutOfRange_TimeSpan()
+        {
+            var largeList = new LargeList();
+
+            TimeSpan infinity = Timeout.InfiniteTimeSpan;
+            Assert.Throws<ArgumentOutOfRangeException>(() => Socket.Select(largeList, null, null, infinity));
+            Assert.Throws<ArgumentOutOfRangeException>(() => Socket.Select(null, largeList, null, infinity));
+            Assert.Throws<ArgumentOutOfRangeException>(() => Socket.Select(null, null, largeList, infinity));
+
+            TimeSpan negative = TimeSpan.FromMilliseconds(-1);
+            Assert.Throws<ArgumentOutOfRangeException>(() => Socket.Select(largeList, null, null, negative));
+            Assert.Throws<ArgumentOutOfRangeException>(() => Socket.Select(null, largeList, null, negative));
+            Assert.Throws<ArgumentOutOfRangeException>(() => Socket.Select(null, null, largeList, negative));
+        }
+
+        [Fact]
         public void AcceptAsync_NullAsyncEventArgs_Throws_ArgumentNull()
         {
             Assert.Throws<ArgumentNullException>(() => GetSocket().AcceptAsync((SocketAsyncEventArgs)null));
index 39506ce..954e70f 100644 (file)
@@ -341,8 +341,10 @@ namespace System.Net.Sockets.Tests
             }
         }
 
-        [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 @@ namespace System.Net.Sockets.Tests
                 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);
index cbcaf58..0b77ecb 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\FormatException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\FormattableString.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Function.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\GC.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\GCMemoryInfo.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Gen2GcCallback.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Calendar.cs" />
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 (file)
index 0000000..de53aa1
--- /dev/null
@@ -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
+    {
+        /// <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 GC approach</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.
+        /// </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 WaitForFullGCComplete(TimeSpan timeout)
+            => WaitForFullGCComplete(WaitHandle.ToTimeoutMilliseconds(timeout));
+    }
+}
index 421af0e..f3aa1a5 100644 (file)
@@ -2637,15 +2637,41 @@ namespace System.Threading.Tasks
         /// infinite time-out -or- timeout is greater than
         /// <see cref="int.MaxValue"/>.
         /// </exception>
-        public bool Wait(TimeSpan timeout)
+        public bool Wait(TimeSpan timeout) => Wait(timeout, default);
+
+        /// <summary>
+        /// Waits for the <see cref="Task"/> to complete execution.
+        /// </summary>
+        /// <param name="timeout">The time to wait, or <see cref="Timeout.InfiniteTimeSpan"/> to wait indefinitely</param>
+        /// <param name="cancellationToken">
+        /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
+        /// </param>
+        /// <returns>
+        /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
+        /// </returns>
+        /// <exception cref="AggregateException">
+        /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
+        /// cref="Task"/>.
+        /// </exception>
+        /// <exception cref="ArgumentOutOfRangeException">
+        /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
+        /// infinite time-out -or- timeout is greater than
+        /// <see cref="int.MaxValue"/>.
+        /// </exception>
+        /// <exception cref="OperationCanceledException">
+        /// The <paramref name="cancellationToken"/> was canceled.
+        /// </exception>
+        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);
         }
 
         /// <summary>
index 0ec230c..b3ae854 100644 (file)
@@ -2440,8 +2440,10 @@ namespace System
         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
@@ -13368,6 +13370,7 @@ namespace System.Threading.Tasks
         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")]
index 1298a7c..42be0a6 100644 (file)
@@ -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) { }
index c8dcaa5..74e9533 100644 (file)
@@ -72,6 +72,26 @@ namespace System.ServiceProcess
             }
         }
 
+#if NETCOREAPP
+        /// <summary>
+        /// 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.
+        /// </summary>
+        /// <param name="time">The requested additional time</param>
+        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
+
         /// <summary>
         /// Indicates whether to report Start, Stop, Pause, and Continue commands in the event.
         /// </summary>
index 247d600..67954fd 100644 (file)
@@ -72,6 +72,18 @@ namespace System.ServiceProcess.Tests
             }
         }
 
+#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<ArgumentOutOfRangeException>("time", () => serviceBase.RequestAdditionalTime(time));
+        }
+#endif
+
         [ConditionalFact(nameof(IsProcessElevated))]
         public void TestOnStartThenStop()
         {
index 2253b5e..ca9d7b8 100644 (file)
@@ -7,6 +7,7 @@
   <ItemGroup>
     <Compile Include="AssemblyInfo.cs" />
     <Compile Include="Helpers.cs" />
+    <Compile Include="Task\TaskArgumentValidationTests.cs" />
     <Compile Include="XunitAssemblyAttributes.cs" />
     <Compile Include="UnwrapTests.cs" />
     <Compile Include="AggregateExceptionTests.cs" />
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 (file)
index 0000000..9989219
--- /dev/null
@@ -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<ArgumentOutOfRangeException>("timeout", () => task.Wait(timeout));
+        }
+    }
+}
index 35f7917..11d2e32 100644 (file)
@@ -256,8 +256,10 @@ namespace System.Threading.Tasks.Tests
         }
 
         // 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 @@ namespace System.Threading.Tasks.Tests
 
             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 @@ namespace System.Threading.Tasks.Tests
             // 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)
             {