From b42148aeae5f49b4b96a851ccf0caeb1728c7499 Mon Sep 17 00:00:00 2001 From: Eric Eilebrecht Date: Wed, 13 Jul 2016 13:09:05 -0700 Subject: [PATCH] Avoid wrapping async I/O in synchronous methods in the HTTP tests, as this can lead to ThreadPool starvation, causing tests to run longer (waiting for the ThreadPool to inject enough threads) and possibly to time out. Commit migrated from https://github.com/dotnet/corefx/commit/af17a7ab4bd0630f71e44bbe566bb712aeb86b5c --- .../Common/tests/System/IO/DelegateStream.cs | 8 +++-- .../tests/FunctionalTests/HttpClientHandlerTest.cs | 38 ++++++++++++++-------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/libraries/Common/tests/System/IO/DelegateStream.cs b/src/libraries/Common/tests/System/IO/DelegateStream.cs index 94c057b..29aa555 100644 --- a/src/libraries/Common/tests/System/IO/DelegateStream.cs +++ b/src/libraries/Common/tests/System/IO/DelegateStream.cs @@ -52,13 +52,17 @@ namespace System.IO _positionSetFunc = positionSetFunc ?? (_ => { throw new NotSupportedException(); }); _positionGetFunc = positionGetFunc ?? (() => { throw new NotSupportedException(); }); - _readFunc = readFunc ?? ((buffer, offset, count) => readAsyncFunc(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult()); + if (readAsyncFunc != null && readFunc == null) + throw new InvalidOperationException("If reads are supported, must provide a synchronous read implementation"); + _readFunc = readFunc; _readAsyncFunc = readAsyncFunc ?? ((buffer, offset, count, token) => base.ReadAsync(buffer, offset, count, token)); _seekFunc = seekFunc ?? ((_, __) => { throw new NotSupportedException(); }); _setLengthFunc = setLengthFunc ?? (_ => { throw new NotSupportedException(); }); - _writeFunc = writeFunc ?? ((buffer, offset, count) => writeAsyncFunc(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult()); + if (writeAsyncFunc != null && writeFunc == null) + throw new InvalidOperationException("If writes are supported, must provide a synchronous write implementation"); + _writeFunc = writeFunc; _writeAsyncFunc = writeAsyncFunc ?? ((buffer, offset, count, token) => base.WriteAsync(buffer, offset, count, token)); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs index e23ed2f..91c9b3f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs @@ -1022,6 +1022,7 @@ namespace System.Net.Http.Functional.Tests lengthFunc: () => wrappedMemStream.Length, positionGetFunc: () => wrappedMemStream.Position, positionSetFunc: p => wrappedMemStream.Position = p, + readFunc: (buffer, offset, count) => wrappedMemStream.Read(buffer, offset, count), readAsyncFunc: (buffer, offset, count, token) => wrappedMemStream.ReadAsync(buffer, offset, count, token)); yield return new object[] { server, new StreamContentWithSyncAsyncCopy(syncKnownLengthStream, syncCopy: syncCopy), data }; } @@ -1029,34 +1030,45 @@ namespace System.Net.Http.Functional.Tests // A stream that provides the data synchronously and has an unknown length { int syncUnknownLengthStreamOffset = 0; + + Func readFunc = (buffer, offset, count) => + { + int bytesRemaining = data.Length - syncUnknownLengthStreamOffset; + int bytesToCopy = Math.Min(bytesRemaining, count); + Array.Copy(data, syncUnknownLengthStreamOffset, buffer, offset, bytesToCopy); + syncUnknownLengthStreamOffset += bytesToCopy; + return bytesToCopy; + }; + var syncUnknownLengthStream = new DelegateStream( canReadFunc: () => true, canSeekFunc: () => false, - readAsyncFunc: (buffer, offset, count, token) => - { - int bytesRemaining = data.Length - syncUnknownLengthStreamOffset; - int bytesToCopy = Math.Min(bytesRemaining, count); - Array.Copy(data, syncUnknownLengthStreamOffset, buffer, offset, bytesToCopy); - syncUnknownLengthStreamOffset += bytesToCopy; - return Task.FromResult(bytesToCopy); - }); + readFunc: readFunc, + readAsyncFunc: (buffer, offset, count, token) => Task.FromResult(readFunc(buffer, offset, count))); yield return new object[] { server, new StreamContentWithSyncAsyncCopy(syncUnknownLengthStream, syncCopy: syncCopy), data }; } // A stream that provides the data asynchronously { int asyncStreamOffset = 0, maxDataPerRead = 100; + + Func readFunc = (buffer, offset, count) => + { + int bytesRemaining = data.Length - asyncStreamOffset; + int bytesToCopy = Math.Min(bytesRemaining, Math.Min(maxDataPerRead, count)); + Array.Copy(data, asyncStreamOffset, buffer, offset, bytesToCopy); + asyncStreamOffset += bytesToCopy; + return bytesToCopy; + }; + var asyncStream = new DelegateStream( canReadFunc: () => true, canSeekFunc: () => false, + readFunc: readFunc, readAsyncFunc: async (buffer, offset, count, token) => { await Task.Delay(1).ConfigureAwait(false); - int bytesRemaining = data.Length - asyncStreamOffset; - int bytesToCopy = Math.Min(bytesRemaining, Math.Min(maxDataPerRead, count)); - Array.Copy(data, asyncStreamOffset, buffer, offset, bytesToCopy); - asyncStreamOffset += bytesToCopy; - return bytesToCopy; + return readFunc(buffer, offset, count); }); yield return new object[] { server, new StreamContentWithSyncAsyncCopy(asyncStream, syncCopy: syncCopy), data }; } -- 2.7.4