Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Random.Shared #50297

Merged
merged 2 commits into from
Apr 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte>(uncompressed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class LoopbackSmtpServer : IDisposable
private bool _disposed = false;
private readonly Socket _listenSocket;
private readonly ConcurrentBag<Socket> _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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,7 @@ public async Task SslStream_TargetHostName_Succeeds(bool useEmptyName)
[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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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));

Expand Down
7 changes: 2 additions & 5 deletions src/libraries/System.Net.WebClient/tests/WebClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte>(sendBuffer);

// Ask the remote server to echo back received messages without ever signaling "end of message".
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ static GenericVectorTests()
// 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)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
103 changes: 103 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Random.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
Expand Down Expand Up @@ -36,6 +39,17 @@ public partial class Random
// previously output continues to be output.
_impl = new LegacyImpl(this, Seed);

/// <summary>Constructor used by <see cref="ThreadSafeRandom"/>.</summary>
/// <param name="isThreadSafeRandom">Must be true.</param>
protected private Random(bool isThreadSafeRandom)
{
Debug.Assert(isThreadSafeRandom);
_impl = null!; // base implementation isn't used at all
}

/// <summary>Provides a thread-safe <see cref="Random"/> instance that may be used concurrently from any thread.</summary>
public static Random Shared { get; } = new ThreadSafeRandom();

/// <summary>Returns a non-negative random integer.</summary>
/// <returns>A 32-bit signed integer that is greater than or equal to 0 and less than <see cref="int.MaxValue"/>.</returns>
public virtual int Next() => _impl.Next();
Expand Down Expand Up @@ -148,5 +162,94 @@ public virtual void NextBytes(byte[] buffer)

private static void ThrowMinMaxValueSwapped() =>
throw new ArgumentOutOfRangeException("minValue", SR.Format(SR.Argument_MinMaxValue, "minValue", "maxValue"));

/// <summary>Random implementation that delegates all calls to a ThreadStatic Impl instance.</summary>
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();
stephentoub marked this conversation as resolved.
Show resolved Hide resolved

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<byte> buffer) => LocalRandom.NextBytes(buffer);

protected override double Sample() => throw new NotSupportedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Loading