Avoid possible tearing in HeartbeatManager.UtcNow (#3092)

This commit is contained in:
Stephen Halter 2018-11-13 11:58:54 -08:00 committed by GitHub
parent 119a7695aa
commit f223b4a663
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 75 additions and 47 deletions

View File

@ -1052,8 +1052,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
if (stream.IsDraining) if (stream.IsDraining)
{ {
stream.DrainExpiration = stream.DrainExpirationTicks =
_context.ServiceContext.SystemClock.UtcNow + Constants.RequestBodyDrainTimeout; _context.ServiceContext.SystemClock.UtcNowTicks + Constants.RequestBodyDrainTimeout.Ticks;
_drainingStreams.TryAdd(streamId, stream); _drainingStreams.TryAdd(streamId, stream);
} }
@ -1096,7 +1096,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
foreach (var stream in _drainingStreams) foreach (var stream in _drainingStreams)
{ {
if (now > stream.Value.DrainExpiration) if (now.Ticks > stream.Value.DrainExpirationTicks)
{ {
RemoveDrainingStream(stream.Key); RemoveDrainingStream(stream.Key);
} }

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private readonly StreamInputFlowControl _inputFlowControl; private readonly StreamInputFlowControl _inputFlowControl;
private readonly StreamOutputFlowControl _outputFlowControl; private readonly StreamOutputFlowControl _outputFlowControl;
internal DateTimeOffset DrainExpiration { get; set; } internal long DrainExpirationTicks { get; set; }
private StreamCompletionFlags _completionState; private StreamCompletionFlags _completionState;
private readonly object _completionLock = new object(); private readonly object _completionLock = new object();

View File

@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
using (connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this)) using (connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this))
{ {
// Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs. // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs.
_timeoutControl.Initialize(_systemClock.UtcNow); _timeoutControl.Initialize(_systemClock.UtcNowTicks);
_context.ConnectionFeatures.Set<IConnectionTimeoutFeature>(_timeoutControl); _context.ConnectionFeatures.Set<IConnectionTimeoutFeature>(_timeoutControl);
@ -356,7 +356,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
return; return;
} }
var now = _systemClock.UtcNow; // It's safe to use UtcNowUnsynchronized since Tick is called by the Heartbeat.
var now = _systemClock.UtcNowUnsynchronized;
_timeoutControl.Tick(now); _timeoutControl.Tick(now);
_requestProcessor?.Tick(now); _requestProcessor?.Tick(now);
} }

View File

@ -2,6 +2,7 @@
// 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.Threading;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
{ {
@ -10,6 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
private readonly ConnectionManager _connectionManager; private readonly ConnectionManager _connectionManager;
private readonly Action<KestrelConnection> _walkCallback; private readonly Action<KestrelConnection> _walkCallback;
private DateTimeOffset _now; private DateTimeOffset _now;
private long _nowTicks;
public HeartbeatManager(ConnectionManager connectionManager) public HeartbeatManager(ConnectionManager connectionManager)
{ {
@ -17,11 +19,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
_walkCallback = WalkCallback; _walkCallback = WalkCallback;
} }
public DateTimeOffset UtcNow => _now; public DateTimeOffset UtcNow => new DateTimeOffset(UtcNowTicks, TimeSpan.Zero);
public long UtcNowTicks => Volatile.Read(ref _nowTicks);
public DateTimeOffset UtcNowUnsynchronized => _now;
public void OnHeartbeat(DateTimeOffset now) public void OnHeartbeat(DateTimeOffset now)
{ {
_now = now; _now = now;
Volatile.Write(ref _nowTicks, now.Ticks);
_connectionManager.Walk(_walkCallback); _connectionManager.Walk(_walkCallback);
} }

View File

@ -11,8 +11,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
public interface ISystemClock public interface ISystemClock
{ {
/// <summary> /// <summary>
/// Retrieves the current system time in UTC. /// Retrieves the current UTC system time.
/// </summary> /// </summary>
DateTimeOffset UtcNow { get; } DateTimeOffset UtcNow { get; }
/// <summary>
/// Retrieves ticks for the current UTC system time.
/// </summary>
long UtcNowTicks { get; }
/// <summary>
/// Retrieves the current UTC system time.
/// This is only safe to use from code called by the <see cref="Heartbeat"/>.
/// </summary>
DateTimeOffset UtcNowUnsynchronized { get; }
} }
} }

View File

@ -11,14 +11,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
internal class SystemClock : ISystemClock internal class SystemClock : ISystemClock
{ {
/// <summary> /// <summary>
/// Retrieves the current system time in UTC. /// Retrieves the current UTC system time.
/// </summary> /// </summary>
public DateTimeOffset UtcNow public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
{
get /// <summary>
{ /// Retrieves ticks for the current UTC system time.
return DateTimeOffset.UtcNow; /// </summary>
} public long UtcNowTicks => DateTimeOffset.UtcNow.Ticks;
}
/// <summary>
/// Retrieves the current UTC system time.
/// </summary>
public DateTimeOffset UtcNowUnsynchronized => DateTimeOffset.UtcNow;
} }
} }

View File

@ -40,9 +40,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
internal IDebugger Debugger { get; set; } = DebuggerWrapper.Singleton; internal IDebugger Debugger { get; set; } = DebuggerWrapper.Singleton;
public void Initialize(DateTimeOffset now) internal void Initialize(long nowTicks)
{ {
_lastTimestamp = now.Ticks; _lastTimestamp = nowTicks;
} }
public void Tick(DateTimeOffset now) public void Tick(DateTimeOffset now)

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_timeoutControl.Debugger = mockDebugger.Object; _timeoutControl.Debugger = mockDebugger.Object;
var now = DateTimeOffset.Now; var now = DateTimeOffset.Now;
_timeoutControl.Initialize(now); _timeoutControl.Initialize(now.Ticks);
_timeoutControl.SetTimeout(1, TimeoutReason.RequestHeaders); _timeoutControl.SetTimeout(1, TimeoutReason.RequestHeaders);
_timeoutControl.Tick(now.AddTicks(2).Add(Heartbeat.Interval)); _timeoutControl.Tick(now.AddTicks(2).Add(Heartbeat.Interval));
@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Initialize timestamp // Initialize timestamp
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
_timeoutControl.Initialize(now); _timeoutControl.Initialize(now.Ticks);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
_timeoutControl.StartTimingRead(); _timeoutControl.StartTimingRead();
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Initialize timestamp // Initialize timestamp
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
_timeoutControl.Initialize(now); _timeoutControl.Initialize(now.Ticks);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
_timeoutControl.StartTimingRead(); _timeoutControl.StartTimingRead();
@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
// Initialize timestamp // Initialize timestamp
_timeoutControl.Initialize(_systemClock.UtcNow); _timeoutControl.Initialize(_systemClock.UtcNow.Ticks);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
_timeoutControl.StartTimingRead(); _timeoutControl.StartTimingRead();
@ -215,7 +215,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
// Initialize timestamp // Initialize timestamp
_timeoutControl.Initialize(_systemClock.UtcNow); _timeoutControl.Initialize(_systemClock.UtcNow.Ticks);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
_timeoutControl.StartTimingRead(); _timeoutControl.StartTimingRead();
@ -263,7 +263,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var startTime = _systemClock.UtcNow; var startTime = _systemClock.UtcNow;
// Initialize timestamp // Initialize timestamp
_timeoutControl.Initialize(startTime); _timeoutControl.Initialize(startTime.Ticks);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
_timeoutControl.StartTimingRead(); _timeoutControl.StartTimingRead();
@ -295,7 +295,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Initialize timestamp // Initialize timestamp
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
_timeoutControl.Initialize(now); _timeoutControl.Initialize(now.Ticks);
_timeoutControl.InitializeHttp2(flowControl); _timeoutControl.InitializeHttp2(flowControl);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
@ -341,7 +341,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Initialize timestamp // Initialize timestamp
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
_timeoutControl.Initialize(now); _timeoutControl.Initialize(now.Ticks);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
_timeoutControl.StartTimingRead(); _timeoutControl.StartTimingRead();
@ -379,7 +379,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
// Initialize timestamp // Initialize timestamp
_timeoutControl.Initialize(_systemClock.UtcNow); _timeoutControl.Initialize(_systemClock.UtcNow.Ticks);
// Should complete within 4 seconds, but the timeout is adjusted by adding Heartbeat.Interval // Should complete within 4 seconds, but the timeout is adjusted by adding Heartbeat.Interval
_timeoutControl.BytesWrittenToBuffer(minRate, 400); _timeoutControl.BytesWrittenToBuffer(minRate, 400);
@ -398,7 +398,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Initialize timestamp // Initialize timestamp
var startTime = _systemClock.UtcNow; var startTime = _systemClock.UtcNow;
_timeoutControl.Initialize(startTime); _timeoutControl.Initialize(startTime.Ticks);
// Should complete within 1 second, but the timeout is adjusted by adding Heartbeat.Interval // Should complete within 1 second, but the timeout is adjusted by adding Heartbeat.Interval
_timeoutControl.BytesWrittenToBuffer(minRate, 100); _timeoutControl.BytesWrittenToBuffer(minRate, 100);
@ -422,7 +422,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
// Initialize timestamp // Initialize timestamp
_timeoutControl.Initialize(_systemClock.UtcNow); _timeoutControl.Initialize(_systemClock.UtcNow.Ticks);
// Should complete within 5 seconds, but the timeout is adjusted by adding Heartbeat.Interval // Should complete within 5 seconds, but the timeout is adjusted by adding Heartbeat.Interval
_timeoutControl.BytesWrittenToBuffer(minRate, 500); _timeoutControl.BytesWrittenToBuffer(minRate, 500);
@ -456,7 +456,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Initialize timestamp // Initialize timestamp
var startTime = _systemClock.UtcNow; var startTime = _systemClock.UtcNow;
_timeoutControl.Initialize(startTime); _timeoutControl.Initialize(startTime.Ticks);
// 5 consecutive 100 byte writes. // 5 consecutive 100 byte writes.
for (var i = 0; i < numWrites - 1; i++) for (var i = 0; i < numWrites - 1; i++)
@ -487,7 +487,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2)); var minRate = new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(2));
// Initialize timestamp // Initialize timestamp
_timeoutControl.Initialize(_systemClock.UtcNow); _timeoutControl.Initialize(_systemClock.UtcNow.Ticks);
// Should complete within 4 seconds, but the timeout is adjusted by adding Heartbeat.Interval // Should complete within 4 seconds, but the timeout is adjusted by adding Heartbeat.Interval
_timeoutControl.BytesWrittenToBuffer(minRate, 400); _timeoutControl.BytesWrittenToBuffer(minRate, 400);
@ -512,7 +512,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var minRate = new MinDataRate(bytesPerSecond, gracePeriod); var minRate = new MinDataRate(bytesPerSecond, gracePeriod);
// Initialize timestamp // Initialize timestamp
_timeoutControl.Initialize(_systemClock.UtcNow); _timeoutControl.Initialize(_systemClock.UtcNow.Ticks);
_timeoutControl.StartRequestBody(minRate); _timeoutControl.StartRequestBody(minRate);
_timeoutControl.StartTimingRead(); _timeoutControl.StartTimingRead();

View File

@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var mockSystemClock = _serviceContext.MockSystemClock; var mockSystemClock = _serviceContext.MockSystemClock;
var limits = _serviceContext.ServerOptions.Limits; var limits = _serviceContext.ServerOptions.Limits;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
CreateConnection(); CreateConnection();
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var mockSystemClock = _serviceContext.MockSystemClock; var mockSystemClock = _serviceContext.MockSystemClock;
var limits = _serviceContext.ServerOptions.Limits; var limits = _serviceContext.ServerOptions.Limits;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_noopApplication); await InitializeConnectionAsync(_noopApplication);
@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var mockSystemClock = _serviceContext.MockSystemClock; var mockSystemClock = _serviceContext.MockSystemClock;
var limits = _serviceContext.ServerOptions.Limits; var limits = _serviceContext.ServerOptions.Limits;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_noopApplication); await InitializeConnectionAsync(_noopApplication);
@ -128,7 +128,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var mockSystemClock = _serviceContext.MockSystemClock; var mockSystemClock = _serviceContext.MockSystemClock;
var limits = _serviceContext.ServerOptions.Limits;; var limits = _serviceContext.ServerOptions.Limits;;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_noopApplication); await InitializeConnectionAsync(_noopApplication);
@ -165,7 +165,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var mockSystemClock = _serviceContext.MockSystemClock; var mockSystemClock = _serviceContext.MockSystemClock;
var limits = _serviceContext.ServerOptions.Limits; var limits = _serviceContext.ServerOptions.Limits;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_noopApplication); await InitializeConnectionAsync(_noopApplication);
@ -272,7 +272,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Disable response buffering so "socket" backpressure is observed immediately. // Disable response buffering so "socket" backpressure is observed immediately.
limits.MaxResponseBufferSize = 0; limits.MaxResponseBufferSize = 0;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -328,7 +328,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Disable response buffering so "socket" backpressure is observed immediately. // Disable response buffering so "socket" backpressure is observed immediately.
limits.MaxResponseBufferSize = 0; limits.MaxResponseBufferSize = 0;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -386,7 +386,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// This only affects the stream windows. The connection-level window is always initialized at 64KiB. // This only affects the stream windows. The connection-level window is always initialized at 64KiB.
_clientSettings.InitialWindowSize = 6; _clientSettings.InitialWindowSize = 6;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -440,7 +440,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// This only affects the stream windows. The connection-level window is always initialized at 64KiB. // This only affects the stream windows. The connection-level window is always initialized at 64KiB.
_clientSettings.InitialWindowSize = (uint)_maxData.Length - 1; _clientSettings.InitialWindowSize = (uint)_maxData.Length - 1;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -496,7 +496,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// This only affects the stream windows. The connection-level window is always initialized at 64KiB. // This only affects the stream windows. The connection-level window is always initialized at 64KiB.
_clientSettings.InitialWindowSize = (uint)_maxData.Length - 1; _clientSettings.InitialWindowSize = (uint)_maxData.Length - 1;
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -561,7 +561,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Use non-default value to ensure the min request and response rates aren't mixed up. // Use non-default value to ensure the min request and response rates aren't mixed up.
limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5));
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -610,7 +610,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Use non-default value to ensure the min request and response rates aren't mixed up. // Use non-default value to ensure the min request and response rates aren't mixed up.
limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5));
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -663,7 +663,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Use non-default value to ensure the min request and response rates aren't mixed up. // Use non-default value to ensure the min request and response rates aren't mixed up.
limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5));
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -732,7 +732,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Use non-default value to ensure the min request and response rates aren't mixed up. // Use non-default value to ensure the min request and response rates aren't mixed up.
limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5));
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(_echoApplication); await InitializeConnectionAsync(_echoApplication);
@ -807,7 +807,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Use non-default value to ensure the min request and response rates aren't mixed up. // Use non-default value to ensure the min request and response rates aren't mixed up.
limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5)); limits.MinRequestBodyDataRate = new MinDataRate(480, TimeSpan.FromSeconds(2.5));
_timeoutControl.Initialize(mockSystemClock.UtcNow); _timeoutControl.Initialize(mockSystemClock.UtcNow.Ticks);
await InitializeConnectionAsync(async context => await InitializeConnectionAsync(async context =>
{ {

View File

@ -24,6 +24,10 @@ namespace Microsoft.AspNetCore.Testing
} }
} }
public long UtcNowTicks => UtcNow.Ticks;
public DateTimeOffset UtcNowUnsynchronized => UtcNow;
public int UtcNowCalled { get; private set; } public int UtcNowCalled { get; private set; }
} }
} }