Skip to content

Instantly share code, notes, and snippets.

@antonfirsov
Created July 31, 2020 16:27
Show Gist options
  • Save antonfirsov/b5deb76ebdc342f6f085206632c47c69 to your computer and use it in GitHub Desktop.
Save antonfirsov/b5deb76ebdc342f6f085206632c47c69 to your computer and use it in GitHub Desktop.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework.Utilities;
using Xunit;
using Xunit.Abstractions;
namespace System.Net.Sockets.Tests
{
public class Repro31570 : SocketTestHelperBase<SocketHelperEap>
{
public Repro31570(ITestOutputHelper output) : base(output)
{
}
public static TheoryData<int> GetDisposeAfterData()
{
TheoryData<int> result = new TheoryData<int>();
for (int i = 0; i < 10; i++)
{
result.Add(8000 + 2 * i);
}
return result;
}
[Theory(Timeout = 5000)]
[MemberData(nameof(GetDisposeAfterData))]
public async Task ReproMaybe(int disposeAfterReceives)
{
byte[] sendBuffer = new byte[512];
byte[] receiveBuffer = new byte[512];
// We need to send enough messages to make sure the OS will stop processing them synchronously,
// so the PAL will actually start queuing BufferMemoryReceiveOperation
int msgCount = 10000;
(Socket client, Socket server) = SocketTestExtensions.CreateConnectedSocketPair();
List<Task> allTasks = new List<Task>();
List<Task> receiveTasks = new List<Task>();
ManualResetEventSlim shouldDisposeNow = new ManualResetEventSlim();
Task disposeTask = Task.Factory.StartNew(() =>
{
shouldDisposeNow.Wait();
Thread.Sleep(1);
client.Shutdown(SocketShutdown.Both);
client.Close(15);
}, TaskCreationOptions.LongRunning);
for (int i = 0; i < msgCount; i++)
{
Task sendTask = SendAsync(server, sendBuffer);
allTasks.Add(sendTask);
}
for (int i = 0; i < msgCount; i++)
{
if (client.SafeHandle.IsClosed)
{
break;
}
Task receiveTask = null;
try
{
receiveTask = ReceiveAsync(client, receiveBuffer);
}
catch (ObjectDisposedException)
{
Console.WriteLine("Disposed. good!");
break;
}
// Sufficient number of receives is completed, and we are sure BufferMemoryReceiveOperations are being queued,
// So we try to disturb the execution of a follow-up receive by a sudden disposal:
if (i == disposeAfterReceives)
{
if (receiveTask.IsCompleted)
{
Console.WriteLine($"Firing the dispose @ {i} [sync]");
shouldDisposeNow.Set();
}
else
{
receiveTask.GetAwaiter().OnCompleted(() =>
{
Console.WriteLine($"Firing dispose @ {i} [async]");
shouldDisposeNow.Set();
});
}
}
allTasks.Add(receiveTask);
receiveTasks.Add(receiveTask);
}
try
{
await Task.WhenAll(receiveTasks);
}
catch (Exception ex)
{
Console.WriteLine($"Got exception: {ex.GetType()} | {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
client.Dispose();
server.Dispose();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment