From d717f3e7fb2a88e0b2ee521fdf036a728643fcf9 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 24 Feb 2021 10:19:38 -0500 Subject: [PATCH 1/2] Add Random.Shared --- .../System/IO/StreamConformanceTests.cs | 2 +- .../Collections/Concurrent/ConcurrentStack.cs | 7 +- .../CompressionStreamUnitTests.Brotli.cs | 2 +- .../tests/Functional/LoopbackSmtpServer.cs | 2 +- .../Net/NetworkInformation/Ping.Unix.cs | 5 +- .../tests/FunctionalTests/QuicStreamTests.cs | 2 +- .../SslStreamNetworkStreamTest.cs | 3 +- .../SocketAsyncEventArgsTest.cs | 2 +- .../FunctionalTests/UnixDomainSocketTest.cs | 9 +- .../tests/WebClientTest.cs | 7 +- .../tests/SendReceiveTest.cs | 4 +- .../tests/GenericVectorTests.cs | 2 +- .../System/Numerics/Hashing/HashHelpers.cs | 2 - .../src/System/Random.cs | 103 ++++++++++++++++++ .../tests/XmlReaderLib/CXMLGeneralTest.cs | 2 +- .../Environment.ExpandEnvironmentVariables.cs | 3 +- .../Environment.GetEnvironmentVariable.cs | 5 +- .../tests/System/Random.cs | 43 ++++++++ .../tests/DataContractSerializer.cs | 2 +- .../System.Runtime/ref/System.Runtime.cs | 1 + .../tests/ECDsaTests.cs | 6 +- 21 files changed, 171 insertions(+), 43 deletions(-) diff --git a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs index 69ba465eab5e1..b6ba47288f47e 100644 --- a/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs +++ b/src/libraries/Common/tests/StreamConformanceTests/System/IO/StreamConformanceTests.cs @@ -2240,7 +2240,7 @@ public virtual async Task ReadWrite_CustomMemoryManager_Success(bool useAsync) Assert.Equal(1024, writeBuffer.Memory.Length); Assert.Equal(writeBuffer.Memory.Length, readBuffer.Memory.Length); - new Random().NextBytes(writeBuffer.Memory.Span); + Random.Shared.NextBytes(writeBuffer.Memory.Span); readBuffer.Memory.Span.Clear(); Task write = useAsync ? diff --git a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs index c3689ab8447e3..04cb4e244f707 100644 --- a/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs +++ b/src/libraries/System.Collections.Concurrent/src/System/Collections/Concurrent/ConcurrentStack.cs @@ -606,7 +606,6 @@ private int TryPopCore(int count, out Node? poppedHead) Node? head; Node next; int backoff = 1; - Random? r = null; while (true) { head = _head; @@ -649,11 +648,7 @@ private int TryPopCore(int count, out Node? poppedHead) if (spin.NextSpinWillYield) { - if (r == null) - { - r = new Random(); - } - backoff = r.Next(1, BACKOFF_MAX_YIELDS); + backoff = Random.Shared.Next(1, BACKOFF_MAX_YIELDS); } else { diff --git a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs index 86b2c42591441..3960233e5eafa 100644 --- a/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs +++ b/src/libraries/System.IO.Compression.Brotli/tests/CompressionStreamUnitTests.Brotli.cs @@ -177,7 +177,7 @@ public void RoundTrip_Chunks() for (int i = 0; i < totalSize; i += chunkSize) { byte[] uncompressed = new byte[chunkSize]; - new Random().NextBytes(uncompressed); + Random.Shared.NextBytes(uncompressed); byte[] compressed = new byte[BrotliEncoder.GetMaxCompressedLength(chunkSize)]; byte[] decompressed = new byte[chunkSize]; var uncompressedSpan = new ReadOnlySpan(uncompressed); diff --git a/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs index 4520e439c4e83..e269cc6eeea77 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs @@ -26,7 +26,7 @@ public class LoopbackSmtpServer : IDisposable private bool _disposed = false; private readonly Socket _listenSocket; private readonly ConcurrentBag _socketsToDispose; - private long _messageCounter = new Random().Next(1000, 2000); + private long _messageCounter = Random.Shared.Next(1000, 2000); public readonly int Port; public SmtpClient CreateClient() => new SmtpClient("localhost", Port); diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs index d4f603904025e..74abb2a4adf25 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.Unix.cs @@ -20,8 +20,6 @@ public partial class Ping private const int MaxIpHeaderLengthInBytes = 60; private static bool SendIpHeader => OperatingSystem.IsMacOS(); private static bool NeedsConnect => OperatingSystem.IsLinux(); - [ThreadStatic] - private static Random? t_idGenerator; private PingReply SendPingCore(IPAddress address, byte[] buffer, int timeout, PingOptions? options) { @@ -51,8 +49,7 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in { // Use a random value as the identifier. This doesn't need to be perfectly random // or very unpredictable, rather just good enough to avoid unexpected conflicts. - Random rand = t_idGenerator ??= new Random(); - ushort id = (ushort)rand.Next(ushort.MaxValue + 1); + ushort id = (ushort)Random.Shared.Next(ushort.MaxValue + 1); IpHeader iph = default; bool ipv4 = address.AddressFamily == AddressFamily.InterNetwork; diff --git a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs index b849b8f919e89..4944ce22d9c23 100644 --- a/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs +++ b/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicStreamTests.cs @@ -435,7 +435,7 @@ private static async Task SendAndReceiveEOFAsync(QuicStream s1, QuicStream s2) public async Task ReadWrite_Random_Success(int readSize, int writeSize) { byte[] testBuffer = new byte[8192]; - new Random().NextBytes(testBuffer); + Random.Shared.NextBytes(testBuffer); await RunClientServer( async clientConnection => diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index ea1e5c5ac35f7..0812e23e72a4b 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -242,8 +242,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( [ActiveIssue("https://github.com/dotnet/runtime/issues/46837", TestPlatforms.OSX)] public async Task SslStream_UntrustedCaWithCustomCallback_OK(bool usePartialChain) { - var rnd = new Random(); - int split = rnd.Next(0, certificates.serverChain.Count - 1); + int split = Random.Shared.Next(0, certificates.serverChain.Count - 1); var clientOptions = new SslClientAuthenticationOptions() { TargetHost = "localhost" }; clientOptions.RemoteCertificateValidationCallback = diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs index 00bed9e00ea1a..8beef969bae1e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketAsyncEventArgsTest.cs @@ -561,7 +561,7 @@ public void AcceptAsync_WithReceiveBuffer_Success() const int acceptBufferSize = acceptBufferOverheadSize + acceptBufferDataSize; byte[] sendBuffer = new byte[acceptBufferDataSize]; - new Random().NextBytes(sendBuffer); + Random.Shared.NextBytes(sendBuffer); SocketAsyncEventArgs acceptArgs = new SocketAsyncEventArgs(); acceptArgs.Completed += OnAcceptCompleted; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs index d6a5963f65816..45bbdc29389fa 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs @@ -16,7 +16,6 @@ namespace System.Net.Sockets.Tests public class UnixDomainSocketTest { private readonly ITestOutputHelper _log; - private static Random _random = new Random(); public UnixDomainSocketTest(ITestOutputHelper output) { @@ -251,7 +250,7 @@ public async Task Socket_SendReceiveAsync_Success() public async Task Socket_SendReceiveAsync_PropagateToStream_Success(int iterations, int writeBufferSize, int readBufferSize) { var writeBuffer = new byte[writeBufferSize * iterations]; - new Random().NextBytes(writeBuffer); + Random.Shared.NextBytes(writeBuffer); var readData = new MemoryStream(); string path = GetRandomNonExistingFilePath(); @@ -317,7 +316,7 @@ public async Task ConcurrentSendReceive(bool forceNonBlocking) const int Iters = 25; byte[] sendData = new byte[Iters]; byte[] receiveData = new byte[sendData.Length]; - new Random().NextBytes(sendData); + Random.Shared.NextBytes(sendData); string path = GetRandomNonExistingFilePath(); @@ -359,7 +358,7 @@ public async Task ConcurrentSendReceiveAsync() const int Iters = 2048; byte[] sendData = new byte[Iters]; byte[] receiveData = new byte[sendData.Length]; - new Random().NextBytes(sendData); + Random.Shared.NextBytes(sendData); string path = GetRandomNonExistingFilePath(); @@ -498,7 +497,7 @@ private static string GetRandomNonExistingFilePath() do { // get random name and append random number of characters to get variable name length. - result = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + new string('A', _random.Next(1, 32))); + result = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + new string('A', Random.Shared.Next(1, 32))); } while (File.Exists(result)); diff --git a/src/libraries/System.Net.WebClient/tests/WebClientTest.cs b/src/libraries/System.Net.WebClient/tests/WebClientTest.cs index 571aefc3d6815..a9b9d80e363fc 100644 --- a/src/libraries/System.Net.WebClient/tests/WebClientTest.cs +++ b/src/libraries/System.Net.WebClient/tests/WebClientTest.cs @@ -660,11 +660,8 @@ public async Task UploadData_LargeData_Success(Uri server) byte[] ignored = await UploadDataAsync(wc, server.ToString(), Encoding.UTF8.GetBytes(largeText)); } - private static string GetRandomText(int length) - { - var rand = new Random(); - return new string(Enumerable.Range(0, 512 * 1024).Select(_ => (char)('a' + rand.Next(0, 26))).ToArray()); - } + private static string GetRandomText(int length) => + new string(Enumerable.Range(0, 512 * 1024).Select(_ => (char)('a' + Random.Shared.Next(0, 26))).ToArray()); [OuterLoop("Uses external servers")] [Theory] diff --git a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs index 430c324f40ae5..787bed60c61c1 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs +++ b/src/libraries/System.Net.WebSockets.Client/tests/SendReceiveTest.cs @@ -91,9 +91,8 @@ public async Task SendReceive_PartialMessageDueToSmallReceiveBuffer_Success(Uri [PlatformSpecific(~TestPlatforms.Browser)] // JS Websocket does not support see issue https://github.com/dotnet/runtime/issues/46983 public async Task SendReceive_PartialMessageBeforeCompleteMessageArrives_Success(Uri server) { - var rand = new Random(); var sendBuffer = new byte[ushort.MaxValue + 1]; - rand.NextBytes(sendBuffer); + Random.Shared.NextBytes(sendBuffer); var sendSegment = new ArraySegment(sendBuffer); // Ask the remote server to echo back received messages without ever signaling "end of message". @@ -464,7 +463,6 @@ public async Task ZeroByteReceive_CompletesWhenDataAvailable(Uri server) { using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output)) { - var rand = new Random(); var ctsDefault = new CancellationTokenSource(TimeOutMilliseconds); // Do a 0-byte receive. It shouldn't complete yet. diff --git a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs index 7031a7341f453..59763ef9d9004 100644 --- a/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs +++ b/src/libraries/System.Numerics.Vectors/tests/GenericVectorTests.cs @@ -716,7 +716,7 @@ private void TestTryCopyToSpan() where T : struct // us to check that we didn't overwrite any part of the destination // if it was too small to contain the entire output. - new Random().NextBytes(MemoryMarshal.AsBytes(destination)); + Random.Shared.NextBytes(MemoryMarshal.AsBytes(destination)); T[] destinationCopy = destination.ToArray(); Assert.False(vector.TryCopyTo(destination.Slice(1))); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs index 160793595f40b..7bf7d5c6d1feb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Hashing/HashHelpers.cs @@ -5,8 +5,6 @@ namespace System.Numerics.Hashing { internal static class HashHelpers { - public static readonly int RandomSeed = new Random().Next(int.MinValue, int.MaxValue); - public static int Combine(int h1, int h2) { // RyuJIT optimizes this to use the ROL instruction diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs index daaa02e93a533..6b8df65bb7aa5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.Runtime.CompilerServices; + namespace System { /// @@ -36,6 +39,17 @@ public Random(int Seed) => // previously output continues to be output. _impl = new LegacyImpl(this, Seed); + /// Constructor used by . + /// Must be true. + protected private Random(bool isThreadSafeRandom) + { + Debug.Assert(isThreadSafeRandom); + _impl = null!; // base implementation isn't used at all + } + + /// Provides a thread-safe instance that may be used concurrently from any thread. + public static Random Shared { get; } = new ThreadSafeRandom(); + /// Returns a non-negative random integer. /// A 32-bit signed integer that is greater than or equal to 0 and less than . public virtual int Next() => _impl.Next(); @@ -148,5 +162,94 @@ private static void ThrowMaxValueMustBeNonNegative() => private static void ThrowMinMaxValueSwapped() => throw new ArgumentOutOfRangeException("minValue", SR.Format(SR.Argument_MinMaxValue, "minValue", "maxValue")); + + /// Random implementation that delegates all calls to a ThreadStatic Impl instance. + private sealed class ThreadSafeRandom : Random + { + // We need Random.Shared to return an instance that is thread-safe, as it may be used from multiple threads. + // It's also possible that one thread could retrieve Shared and pass it to another thread, so Shared can't + // just access a ThreadStatic to return a Random instance stored there. As such, we need the instance + // returned from Shared itself to be thread-safe, which can be accomplished either by locking around all + // accesses or by itself accessing a ThreadStatic on every access: we've chosen the latter, as it is more + // scalable. With that, we have two basic approaches: + // 1. the instance returned can be a base Random instance constructed with an _impl that is a ThreadSafeImpl. + // 2. the instance returned can be a special Random-derived instance, where _impl is ignored and the derived + // type overrides all methods on the base. + // (1) is cleaner, as (2) requires duplicating a bit more code, but (2) enables all virtual dispatch to be + // devirtualized and potentially inlined, so that Random.Shared.NextXx(...) ends up being faster. + // This implements (2). + + [ThreadStatic] + private static XoshiroImpl? t_random; + + public ThreadSafeRandom() : base(isThreadSafeRandom: true) { } + + private static XoshiroImpl LocalRandom => t_random ?? Create(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static XoshiroImpl Create() => t_random = new(); + + public override int Next() => LocalRandom.Next(); + + public override int Next(int maxValue) + { + if (maxValue < 0) + { + ThrowMaxValueMustBeNonNegative(); + } + + return LocalRandom.Next(maxValue); + } + + public override int Next(int minValue, int maxValue) + { + if (minValue > maxValue) + { + ThrowMinMaxValueSwapped(); + } + + return LocalRandom.Next(minValue, maxValue); + } + + public override long NextInt64() => LocalRandom.NextInt64(); + + public override long NextInt64(long maxValue) + { + if (maxValue < 0) + { + ThrowMaxValueMustBeNonNegative(); + } + + return LocalRandom.NextInt64(maxValue); + } + + public override long NextInt64(long minValue, long maxValue) + { + if (minValue > maxValue) + { + ThrowMinMaxValueSwapped(); + } + + return LocalRandom.NextInt64(minValue, maxValue); + } + + public override float NextSingle() => LocalRandom.NextSingle(); + + public override double NextDouble() => LocalRandom.NextDouble(); + + public override void NextBytes(byte[] buffer) + { + if (buffer is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.buffer); + } + + LocalRandom.NextBytes(buffer); + } + + public override void NextBytes(Span buffer) => LocalRandom.NextBytes(buffer); + + protected override double Sample() => throw new NotSupportedException(); + } } } diff --git a/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs b/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs index fc89feabd262a..70c84ffe6605b 100644 --- a/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs +++ b/src/libraries/System.Private.Xml/tests/XmlReaderLib/CXMLGeneralTest.cs @@ -237,7 +237,7 @@ public bool CheckCanReadBinaryContent() try { int nBytes = 0; - switch ((int)new Random().Next(4)) + switch ((int)Random.Shared.Next(4)) { case 0: CError.WriteLineIgnore("Selecting RCABH"); diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs index 7cb8b1c610144..4dbda049a7074 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.ExpandEnvironmentVariables.cs @@ -27,8 +27,7 @@ public void ExpansionOfVariableSucceeds() // envvar1=animal; // and we are going to check that the expanded %envvar1% is animal. - Random r = new Random(); - string envVar1 = "TestVariable_ExpansionOfVariableSucceeds_" + r.Next().ToString(); + string envVar1 = "TestVariable_ExpansionOfVariableSucceeds_" + Random.Shared.Next().ToString(); string expectedValue = "animal"; try diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs b/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs index 2f8a5a4800e18..854862bd7f370 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Environment.GetEnvironmentVariable.cs @@ -102,9 +102,8 @@ public void VariableNamesAreCaseInsensitiveAsAppropriate() [Fact] public void CanGetAllVariablesIndividually() { - Random r = new Random(); - string envVar1 = "TestVariable_CanGetVariablesIndividually_" + r.Next().ToString(); - string envVar2 = "TestVariable_CanGetVariablesIndividually_" + r.Next().ToString(); + string envVar1 = "TestVariable_CanGetVariablesIndividually_" + Random.Shared.Next().ToString(); + string envVar2 = "TestVariable_CanGetVariablesIndividually_" + Random.Shared.Next().ToString(); try { diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs index 1d39866627b1f..1a57a30d6af01 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs @@ -6,6 +6,8 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Xunit; namespace System.Tests @@ -388,6 +390,47 @@ public void Empty_Success(bool derived, bool seeded) r.NextBytes(Span.Empty); } + [Fact] + public void Shared_IsSingleton() + { + Assert.NotNull(Random.Shared); + Assert.Same(Random.Shared, Random.Shared); + Assert.Same(Random.Shared, Task.Run(() => Random.Shared).Result); + } + + [Fact] + public void Shared_ParallelUsage() + { + using var barrier = new Barrier(2); + Parallel.For(0, 2, _ => + { + byte[] buffer = new byte[1000]; + + barrier.SignalAndWait(); + for (int i = 0; i < 1_000; i++) + { + Assert.InRange(Random.Shared.Next(), 0, int.MaxValue - 1); + Assert.InRange(Random.Shared.Next(5), 0, 4); + Assert.InRange(Random.Shared.Next(42, 50), 42, 49); + + Assert.InRange(Random.Shared.NextInt64(), 0, long.MaxValue - 1); + Assert.InRange(Random.Shared.NextInt64(5), 0L, 5L); + Assert.InRange(Random.Shared.NextInt64(42L, 50L), 42L, 49L); + + Assert.InRange(Random.Shared.NextSingle(), 0.0f, 1.0f); + Assert.InRange(Random.Shared.NextDouble(), 0.0, 1.0); + + Array.Clear(buffer, 0, buffer.Length); + Random.Shared.NextBytes(buffer); + Assert.Contains(buffer, b => b != 0); + + Array.Clear(buffer, 0, buffer.Length); + Random.Shared.NextBytes((Span)buffer); + Assert.Contains(buffer, b => b != 0); + } + }); + } + [ConditionalFact(typeof(BitConverter), nameof(BitConverter.IsLittleEndian))] // test makes little-endian assumptions public void Xoshiro_AlgorithmBehavesAsExpected() { diff --git a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs index 7adea31ce02fc..91d7ee5312116 100644 --- a/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs +++ b/src/libraries/System.Runtime.Serialization.Xml/tests/DataContractSerializer.cs @@ -3812,7 +3812,7 @@ public static void DCS_BasicPerSerializerRoundTripAndCompare_EnumStruct() foreach (var type in typelist) { var possibleValues = Enum.GetValues(type); - var input = possibleValues.GetValue(new Random().Next(possibleValues.Length)); + var input = possibleValues.GetValue(Random.Shared.Next(possibleValues.Length)); string baseline = $"<_data i:type=\"a:{type}***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/{type}***\">{input}<_data2 i:type=\"a:{type}***\" xmlns:a=\"http://schemas.datacontract.org/2004/07/{type}***\">{input}"; var value = new SerializationTestTypes.ObjectContainer(input); var actual = DataContractSerializerHelper.SerializeAndDeserialize(value, baseline, setting); diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index ef66619698d11..ba59b08890fb2 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3191,6 +3191,7 @@ public virtual void NextBytes(System.Span buffer) { } public virtual float NextSingle() { throw null; } public virtual double NextDouble() { throw null; } protected virtual double Sample() { throw null; } + public static System.Random Shared { get { throw null; } } } public readonly partial struct Range : System.IEquatable { diff --git a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs index cd74668de7c1e..b1bcea4272808 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs +++ b/src/libraries/System.Security.Cryptography.Algorithms/tests/ECDsaTests.cs @@ -78,7 +78,7 @@ public void Array_SignData_VerifyData_UsesHashDataAndSignHashAndVerifyHash() AssertExtensions.Throws("hashAlgorithm", () => ecdsa.VerifyData(new byte[1], 0, 1, new byte[1], new HashAlgorithmName(""))); var input = new byte[1024]; - new Random().NextBytes(input); + Random.Shared.NextBytes(input); byte[] result = ecdsa.SignData(input, HashAlgorithmName.SHA256); Assert.NotNull(result); @@ -104,7 +104,7 @@ public void Stream_SignData_VerifyData_UsesHashDataAndSignHashAndVerifyHash() AssertExtensions.Throws("hashAlgorithm", () => ecdsa.VerifyData(new MemoryStream(new byte[1]), new byte[1], new HashAlgorithmName(""))); var input = new byte[1024]; - new Random().NextBytes(input); + Random.Shared.NextBytes(input); byte[] result = ecdsa.SignData(new MemoryStream(input), HashAlgorithmName.SHA256); Assert.NotNull(result); @@ -126,7 +126,7 @@ public void Span_TrySignData_VerifyData_UsesTryHashDataAndTrySignHashAndTryVerif AssertExtensions.Throws("hashAlgorithm", () => ecdsa.VerifyData((ReadOnlySpan)new byte[1], new byte[1], new HashAlgorithmName(null))); var input = new byte[1024]; - new Random().NextBytes(input); + Random.Shared.NextBytes(input); byte[] output = new byte[1]; int outputLength; From 2020a9fa0a87b9f379978c83fe25665af8d0ec8a Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sat, 27 Mar 2021 20:44:53 -0400 Subject: [PATCH 2/2] Disable on wasm test requiring parallelism --- .../System.Runtime.Extensions/tests/System/Random.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs index 1a57a30d6af01..8a6491aef0530 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs @@ -390,7 +390,7 @@ public void Empty_Success(bool derived, bool seeded) r.NextBytes(Span.Empty); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Shared_IsSingleton() { Assert.NotNull(Random.Shared); @@ -398,7 +398,7 @@ public void Shared_IsSingleton() Assert.Same(Random.Shared, Task.Run(() => Random.Shared).Result); } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void Shared_ParallelUsage() { using var barrier = new Barrier(2);