Prevent enumeration of queue during modification in SocketOutputTests
This commit is contained in:
parent
0c52529b97
commit
fbcb5dcb1b
|
|
@ -2,10 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel;
|
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
// This should match _maxBytesPreCompleted in SocketOutput
|
// This should match _maxBytesPreCompleted in SocketOutput
|
||||||
var maxBytesPreCompleted = 65536;
|
var maxBytesPreCompleted = 65536;
|
||||||
var completeQueue = new Queue<Action<int>>();
|
var completeQueue = new ConcurrentQueue<Action<int>>();
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockLibuv = new MockLibuv
|
var mockLibuv = new MockLibuv
|
||||||
|
|
@ -87,7 +87,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
||||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||||
var ltp = new LoggingThreadPool(trace);
|
var ltp = new LoggingThreadPool(trace);
|
||||||
var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue<UvWriteReq>());
|
var mockConnection = new MockConnection();
|
||||||
|
var socketOutput = new SocketOutput(kestrelThread, socket, memory, mockConnection, "0", trace, ltp, new Queue<UvWriteReq>());
|
||||||
|
|
||||||
var bufferSize = maxBytesPreCompleted;
|
var bufferSize = maxBytesPreCompleted;
|
||||||
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
|
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
|
||||||
|
|
@ -111,7 +112,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
// Too many bytes are already pre-completed for the second write to pre-complete.
|
// Too many bytes are already pre-completed for the second write to pre-complete.
|
||||||
Assert.False(completedWh.Wait(1000));
|
Assert.False(completedWh.Wait(1000));
|
||||||
// Act
|
// Act
|
||||||
completeQueue.Dequeue()(0);
|
Action<int> triggerNextCompleted;
|
||||||
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(0);
|
||||||
// Assert
|
// Assert
|
||||||
// Finishing the first write should allow the second write to pre-complete.
|
// Finishing the first write should allow the second write to pre-complete.
|
||||||
Assert.True(completedWh.Wait(1000));
|
Assert.True(completedWh.Wait(1000));
|
||||||
|
|
@ -120,6 +123,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var cleanupTask = socketOutput.WriteAsync(
|
var cleanupTask = socketOutput.WriteAsync(
|
||||||
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
||||||
|
|
||||||
|
// Wait for all writes to complete so the completeQueue isn't modified during enumeration.
|
||||||
|
Assert.True(mockConnection.SocketClosed.Wait(1000));
|
||||||
|
|
||||||
foreach (var triggerCompleted in completeQueue)
|
foreach (var triggerCompleted in completeQueue)
|
||||||
{
|
{
|
||||||
triggerCompleted(0);
|
triggerCompleted(0);
|
||||||
|
|
@ -132,7 +138,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
// This should match _maxBytesPreCompleted in SocketOutput
|
// This should match _maxBytesPreCompleted in SocketOutput
|
||||||
var maxBytesPreCompleted = 65536;
|
var maxBytesPreCompleted = 65536;
|
||||||
var completeQueue = new Queue<Action<int>>();
|
var completeQueue = new ConcurrentQueue<Action<int>>();
|
||||||
var writeRequestedWh = new ManualResetEventSlim();
|
var writeRequestedWh = new ManualResetEventSlim();
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -155,7 +161,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
||||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||||
var ltp = new LoggingThreadPool(trace);
|
var ltp = new LoggingThreadPool(trace);
|
||||||
var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue<UvWriteReq>());
|
var mockConnection = new MockConnection();
|
||||||
|
var socketOutput = new SocketOutput(kestrelThread, socket, memory, mockConnection, "0", trace, ltp, new Queue<UvWriteReq>());
|
||||||
|
|
||||||
var bufferSize = maxBytesPreCompleted / 2;
|
var bufferSize = maxBytesPreCompleted / 2;
|
||||||
var data = new byte[bufferSize];
|
var data = new byte[bufferSize];
|
||||||
|
|
@ -185,7 +192,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
Assert.Equal(2, completeQueue.Count);
|
Assert.Equal(2, completeQueue.Count);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
completeQueue.Dequeue()(0);
|
Action<int> triggerNextCompleted;
|
||||||
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Finishing the first write should allow the second write to pre-complete.
|
// Finishing the first write should allow the second write to pre-complete.
|
||||||
|
|
@ -195,6 +204,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var cleanupTask = socketOutput.WriteAsync(
|
var cleanupTask = socketOutput.WriteAsync(
|
||||||
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
||||||
|
|
||||||
|
// Wait for all writes to complete so the completeQueue isn't modified during enumeration.
|
||||||
|
Assert.True(mockConnection.SocketClosed.Wait(1000));
|
||||||
|
|
||||||
foreach (var triggerCompleted in completeQueue)
|
foreach (var triggerCompleted in completeQueue)
|
||||||
{
|
{
|
||||||
triggerCompleted(0);
|
triggerCompleted(0);
|
||||||
|
|
@ -207,7 +219,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
// This should match _maxBytesPreCompleted in SocketOutput
|
// This should match _maxBytesPreCompleted in SocketOutput
|
||||||
var maxBytesPreCompleted = 65536;
|
var maxBytesPreCompleted = 65536;
|
||||||
var completeQueue = new Queue<Action<int>>();
|
var completeQueue = new ConcurrentQueue<Action<int>>();
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockLibuv = new MockLibuv
|
var mockLibuv = new MockLibuv
|
||||||
|
|
@ -298,12 +310,19 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
Assert.False(task6Success.IsCanceled);
|
Assert.False(task6Success.IsCanceled);
|
||||||
Assert.False(task6Success.IsFaulted);
|
Assert.False(task6Success.IsFaulted);
|
||||||
|
|
||||||
Assert.True(true);
|
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
var cleanupTask = ((SocketOutput)socketOutput).WriteAsync(
|
var cleanupTask = ((SocketOutput)socketOutput).WriteAsync(
|
||||||
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
||||||
|
|
||||||
|
// Allow for the socketDisconnect command to get posted to the libuv thread.
|
||||||
|
// Right now, the up to three pending writes are holding it up.
|
||||||
|
Action<int> triggerNextCompleted;
|
||||||
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(0);
|
||||||
|
|
||||||
|
// Wait for all writes to complete so the completeQueue isn't modified during enumeration.
|
||||||
|
Assert.True(mockConnection.SocketClosed.Wait(1000));
|
||||||
|
|
||||||
foreach (var triggerCompleted in completeQueue)
|
foreach (var triggerCompleted in completeQueue)
|
||||||
{
|
{
|
||||||
triggerCompleted(0);
|
triggerCompleted(0);
|
||||||
|
|
@ -317,7 +336,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
// This should match _maxBytesPreCompleted in SocketOutput
|
// This should match _maxBytesPreCompleted in SocketOutput
|
||||||
var maxBytesPreCompleted = 65536;
|
var maxBytesPreCompleted = 65536;
|
||||||
var completeQueue = new Queue<Action<int>>();
|
var completeQueue = new ConcurrentQueue<Action<int>>();
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
var mockLibuv = new MockLibuv
|
var mockLibuv = new MockLibuv
|
||||||
|
|
@ -376,7 +395,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
Assert.False(task3Canceled.IsFaulted);
|
Assert.False(task3Canceled.IsFaulted);
|
||||||
|
|
||||||
// Cause the first write to fail.
|
// Cause the first write to fail.
|
||||||
completeQueue.Dequeue()(-1);
|
Action<int> triggerNextCompleted;
|
||||||
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(-1);
|
||||||
|
|
||||||
// Second task is now completed
|
// Second task is now completed
|
||||||
await task2Success;
|
await task2Success;
|
||||||
|
|
@ -389,6 +410,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var cleanupTask = ((SocketOutput)socketOutput).WriteAsync(
|
var cleanupTask = ((SocketOutput)socketOutput).WriteAsync(
|
||||||
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
||||||
|
|
||||||
|
// Wait for all writes to complete so the completeQueue isn't modified during enumeration.
|
||||||
|
Assert.True(mockConnection.SocketClosed.Wait(1000));
|
||||||
|
|
||||||
foreach (var triggerCompleted in completeQueue)
|
foreach (var triggerCompleted in completeQueue)
|
||||||
{
|
{
|
||||||
triggerCompleted(0);
|
triggerCompleted(0);
|
||||||
|
|
@ -402,7 +426,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
// This should match _maxBytesPreCompleted in SocketOutput
|
// This should match _maxBytesPreCompleted in SocketOutput
|
||||||
var maxBytesPreCompleted = 65536;
|
var maxBytesPreCompleted = 65536;
|
||||||
var completeQueue = new Queue<Action<int>>();
|
var completeQueue = new ConcurrentQueue<Action<int>>();
|
||||||
var onWriteWh = new ManualResetEventSlim();
|
var onWriteWh = new ManualResetEventSlim();
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -426,7 +450,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
||||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||||
var ltp = new LoggingThreadPool(trace);
|
var ltp = new LoggingThreadPool(trace);
|
||||||
var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue<UvWriteReq>());
|
var mockConnection = new MockConnection();
|
||||||
|
var socketOutput = new SocketOutput(kestrelThread, socket, memory, mockConnection, "0", trace, ltp, new Queue<UvWriteReq>());
|
||||||
|
|
||||||
var bufferSize = maxBytesPreCompleted;
|
var bufferSize = maxBytesPreCompleted;
|
||||||
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
|
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
|
||||||
|
|
@ -460,7 +485,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
socketOutput.WriteAsync(buffer, default(CancellationToken)).ContinueWith(onCompleted2);
|
socketOutput.WriteAsync(buffer, default(CancellationToken)).ContinueWith(onCompleted2);
|
||||||
|
|
||||||
Assert.True(onWriteWh.Wait(1000));
|
Assert.True(onWriteWh.Wait(1000));
|
||||||
completeQueue.Dequeue()(0);
|
Action<int> triggerNextCompleted;
|
||||||
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Too many bytes are already pre-completed for the third but not the second write to pre-complete.
|
// Too many bytes are already pre-completed for the third but not the second write to pre-complete.
|
||||||
|
|
@ -469,7 +496,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
Assert.False(completedWh2.Wait(1000));
|
Assert.False(completedWh2.Wait(1000));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
completeQueue.Dequeue()(0);
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(0);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Finishing the first write should allow the second write to pre-complete.
|
// Finishing the first write should allow the second write to pre-complete.
|
||||||
|
|
@ -479,6 +507,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var cleanupTask = ((SocketOutput)socketOutput).WriteAsync(
|
var cleanupTask = ((SocketOutput)socketOutput).WriteAsync(
|
||||||
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
||||||
|
|
||||||
|
// Wait for all writes to complete so the completeQueue isn't modified during enumeration.
|
||||||
|
Assert.True(mockConnection.SocketClosed.Wait(1000));
|
||||||
|
|
||||||
foreach (var triggerCompleted in completeQueue)
|
foreach (var triggerCompleted in completeQueue)
|
||||||
{
|
{
|
||||||
triggerCompleted(0);
|
triggerCompleted(0);
|
||||||
|
|
@ -543,7 +574,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
public void OnlyAllowsUpToThreeConcurrentWrites()
|
public void OnlyAllowsUpToThreeConcurrentWrites()
|
||||||
{
|
{
|
||||||
var writeWh = new ManualResetEventSlim();
|
var writeWh = new ManualResetEventSlim();
|
||||||
var completeQueue = new Queue<Action<int>>();
|
var completeQueue = new ConcurrentQueue<Action<int>>();
|
||||||
|
|
||||||
var mockLibuv = new MockLibuv
|
var mockLibuv = new MockLibuv
|
||||||
{
|
{
|
||||||
|
|
@ -564,7 +595,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace());
|
||||||
var trace = new KestrelTrace(new TestKestrelTrace());
|
var trace = new KestrelTrace(new TestKestrelTrace());
|
||||||
var ltp = new LoggingThreadPool(trace);
|
var ltp = new LoggingThreadPool(trace);
|
||||||
var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue<UvWriteReq>());
|
var mockConnection = new MockConnection();
|
||||||
|
var socketOutput = new SocketOutput(kestrelThread, socket, memory, mockConnection, "0", trace, ltp, new Queue<UvWriteReq>());
|
||||||
|
|
||||||
var buffer = new ArraySegment<byte>(new byte[1]);
|
var buffer = new ArraySegment<byte>(new byte[1]);
|
||||||
|
|
||||||
|
|
@ -584,13 +616,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
Assert.False(writeWh.Wait(1000));
|
Assert.False(writeWh.Wait(1000));
|
||||||
|
|
||||||
// Complete 1st write allowing uv_write to be triggered again
|
// Complete 1st write allowing uv_write to be triggered again
|
||||||
completeQueue.Dequeue()(0);
|
Action<int> triggerNextCompleted;
|
||||||
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(0);
|
||||||
Assert.True(writeWh.Wait(1000));
|
Assert.True(writeWh.Wait(1000));
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
var cleanupTask = socketOutput.WriteAsync(
|
var cleanupTask = socketOutput.WriteAsync(
|
||||||
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
default(ArraySegment<byte>), default(CancellationToken), socketDisconnect: true);
|
||||||
|
|
||||||
|
// Allow for the socketDisconnect command to get posted to the libuv thread.
|
||||||
|
// Right now, the three pending writes are holding it up.
|
||||||
|
Assert.True(completeQueue.TryDequeue(out triggerNextCompleted));
|
||||||
|
triggerNextCompleted(0);
|
||||||
|
// Wait for all writes to complete so the completeQueue isn't modified during enumeration.
|
||||||
|
Assert.True(mockConnection.SocketClosed.Wait(1000));
|
||||||
|
|
||||||
foreach (var triggerCompleted in completeQueue)
|
foreach (var triggerCompleted in completeQueue)
|
||||||
{
|
{
|
||||||
triggerCompleted(0);
|
triggerCompleted(0);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue