Skip to content

Commit 6c687e7

Browse files
committed
Merged PR 36009: Fix time scale
We converted `UtcNow.Ticks` usage to `Environment.TickCount64` usage. `TickCount64` is in milliseconds and `UtcNow.Ticks` is in the 100-nanosecond scale. When we made this change we didn't convert `TickCount64` to the same scale so comparisons were wrong.
1 parent c231018 commit 6c687e7

File tree

4 files changed

+40
-18
lines changed

4 files changed

+40
-18
lines changed

src/SignalR/common/Http.Connections/src/HttpConnectionDispatcherOptions.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ public TimeSpan TransportSendTimeout
123123
}
124124

125125
_transportSendTimeout = value;
126-
TransportSendTimeoutTicks = value.Ticks;
127126
}
128127
}
129128

@@ -136,7 +135,6 @@ public TimeSpan TransportSendTimeout
136135
/// </remarks>
137136
public bool CloseOnAuthenticationExpiration { get; set; }
138137

139-
internal long TransportSendTimeoutTicks { get; private set; }
140138
internal bool TransportSendTimeoutEnabled => _transportSendTimeout != Timeout.InfiniteTimeSpan;
141139

142140
// We initialize these lazily based on the state of the options specified here.

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionContext.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ internal class HttpConnectionContext : ConnectionContext,
4848

4949
private CancellationTokenSource? _sendCts;
5050
private bool _activeSend;
51-
private long _startedSendTime;
51+
private TimeSpan _startedSendTime;
5252
private readonly object _sendingLock = new object();
5353
internal CancellationToken SendingToken { get; private set; }
5454

@@ -68,7 +68,7 @@ public HttpConnectionContext(string connectionId, string connectionToken, ILogge
6868

6969
ConnectionId = connectionId;
7070
ConnectionToken = connectionToken;
71-
LastSeenTicks = Environment.TickCount64;
71+
LastSeenTicks = TimeSpan.FromMilliseconds(Environment.TickCount64);
7272
_options = options;
7373

7474
// The default behavior is that both formats are supported.
@@ -121,9 +121,9 @@ public HttpConnectionContext(string connectionId, string connectionToken, ILogge
121121

122122
public Task? ApplicationTask { get; set; }
123123

124-
public long LastSeenTicks { get; set; }
124+
public TimeSpan LastSeenTicks { get; set; }
125125

126-
public long? LastSeenTicksIfInactive
126+
public TimeSpan? LastSeenTicksIfInactive
127127
{
128128
get
129129
{
@@ -544,7 +544,7 @@ public void MarkInactive()
544544
if (Status == HttpConnectionStatus.Active)
545545
{
546546
Status = HttpConnectionStatus.Inactive;
547-
LastSeenTicks = Environment.TickCount64;
547+
LastSeenTicks = TimeSpan.FromMilliseconds(Environment.TickCount64);
548548
}
549549
}
550550
}
@@ -576,12 +576,12 @@ internal void StartSendCancellation()
576576
_sendCts = new CancellationTokenSource();
577577
SendingToken = _sendCts.Token;
578578
}
579-
_startedSendTime = Environment.TickCount64;
579+
_startedSendTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
580580
_activeSend = true;
581581
}
582582
}
583583

584-
internal void TryCancelSend(long currentTicks)
584+
internal void TryCancelSend(TimeSpan currentTicks)
585585
{
586586
if (!_options.TransportSendTimeoutEnabled)
587587
{
@@ -592,7 +592,7 @@ internal void TryCancelSend(long currentTicks)
592592
{
593593
if (_activeSend)
594594
{
595-
if (currentTicks - _startedSendTime > _options.TransportSendTimeoutTicks)
595+
if (currentTicks - _startedSendTime > _options.TransportSendTimeout)
596596
{
597597
_sendCts!.Cancel();
598598

src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ internal partial class HttpConnectionManager
2424
private readonly PeriodicTimer _nextHeartbeat;
2525
private readonly ILogger<HttpConnectionManager> _logger;
2626
private readonly ILogger<HttpConnectionContext> _connectionLogger;
27-
private readonly long _disconnectTimeoutTicks;
27+
private readonly TimeSpan _disconnectTimeout;
2828

2929
public HttpConnectionManager(ILoggerFactory loggerFactory, IHostApplicationLifetime appLifetime, IOptions<ConnectionOptions> connectionOptions)
3030
{
3131
_logger = loggerFactory.CreateLogger<HttpConnectionManager>();
3232
_connectionLogger = loggerFactory.CreateLogger<HttpConnectionContext>();
3333
_nextHeartbeat = new PeriodicTimer(_heartbeatTickRate);
34-
_disconnectTimeoutTicks = (long)(connectionOptions.Value.DisconnectTimeout ?? ConnectionOptionsSetup.DefaultDisconectTimeout).TotalMilliseconds;
34+
_disconnectTimeout = connectionOptions.Value.DisconnectTimeout ?? ConnectionOptionsSetup.DefaultDisconectTimeout;
3535

3636
// Register these last as the callbacks could run immediately
3737
appLifetime.ApplicationStarted.Register(() => Start());
@@ -134,7 +134,7 @@ private async Task ExecuteTimerLoop()
134134
public void Scan()
135135
{
136136
var now = DateTimeOffset.UtcNow;
137-
var ticks = Environment.TickCount64;
137+
var ticks = TimeSpan.FromMilliseconds(Environment.TickCount64);
138138

139139
// Scan the registered connections looking for ones that have timed out
140140
foreach (var c in _connections)
@@ -145,7 +145,7 @@ public void Scan()
145145

146146
// Once the decision has been made to dispose we don't check the status again
147147
// But don't clean up connections while the debugger is attached.
148-
if (!Debugger.IsAttached && lastSeenTick.HasValue && (ticks - lastSeenTick.Value) > _disconnectTimeoutTicks)
148+
if (!Debugger.IsAttached && lastSeenTick.HasValue && (ticks - lastSeenTick.Value) > _disconnectTimeout)
149149
{
150150
Log.ConnectionTimedOut(_logger, connection.ConnectionId);
151151
HttpConnectionsEventSource.Log.ConnectionTimedOut(connection.ConnectionId);

src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ public async Task TransportEndingGracefullyWaitsOnApplicationLongPolling()
552552
await task.DefaultTimeout();
553553

554554
// We've been gone longer than the expiration time
555-
connection.LastSeenTicks = Environment.TickCount64 - (long)disconnectTimeout.TotalMilliseconds - 1;
555+
connection.LastSeenTicks = TimeSpan.FromMilliseconds(Environment.TickCount64) - disconnectTimeout - TimeSpan.FromTicks(1);
556556

557557
// The application is still running here because the poll is only killed
558558
// by the heartbeat so we pretend to do a scan and this should force the application task to complete
@@ -1145,6 +1145,7 @@ bool ExpectedErrors(WriteContext writeContext)
11451145

11461146
using (StartVerifiableLog(expectedErrorsFilter: ExpectedErrors))
11471147
{
1148+
var initialTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
11481149
var manager = CreateConnectionManager(LoggerFactory);
11491150
var connection = manager.CreateConnection();
11501151
connection.TransportType = HttpTransportType.LongPolling;
@@ -1163,8 +1164,15 @@ bool ExpectedErrors(WriteContext writeContext)
11631164
var dispatcherTask = dispatcher.ExecuteAsync(context, options, app);
11641165
await connection.Transport.Output.WriteAsync(new byte[] { 1 }).DefaultTimeout();
11651166
await sync.WaitForSyncPoint().DefaultTimeout();
1167+
1168+
// Try cancel before cancellation should occur
1169+
connection.TryCancelSend(initialTime + options.TransportSendTimeout);
1170+
Assert.False(connection.SendingToken.IsCancellationRequested);
1171+
11661172
// Cancel write to response body
1167-
connection.TryCancelSend(long.MaxValue);
1173+
connection.TryCancelSend(TimeSpan.FromMilliseconds(Environment.TickCount64) + options.TransportSendTimeout + TimeSpan.FromTicks(1));
1174+
Assert.True(connection.SendingToken.IsCancellationRequested);
1175+
11681176
sync.Continue();
11691177
await dispatcherTask.DefaultTimeout();
11701178
// Connection should be removed on canceled write
@@ -1178,6 +1186,7 @@ public async Task SSEConnectionClosesWhenSendTimeoutReached()
11781186
{
11791187
using (StartVerifiableLog())
11801188
{
1189+
var initialTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
11811190
var manager = CreateConnectionManager(LoggerFactory);
11821191
var connection = manager.CreateConnection();
11831192
connection.TransportType = HttpTransportType.ServerSentEvents;
@@ -1195,8 +1204,15 @@ public async Task SSEConnectionClosesWhenSendTimeoutReached()
11951204
var dispatcherTask = dispatcher.ExecuteAsync(context, options, app);
11961205
await connection.Transport.Output.WriteAsync(new byte[] { 1 }).DefaultTimeout();
11971206
await sync.WaitForSyncPoint().DefaultTimeout();
1207+
1208+
// Try cancel before cancellation should occur
1209+
connection.TryCancelSend(initialTime + options.TransportSendTimeout);
1210+
Assert.False(connection.SendingToken.IsCancellationRequested);
1211+
11981212
// Cancel write to response body
1199-
connection.TryCancelSend(long.MaxValue);
1213+
connection.TryCancelSend(TimeSpan.FromMilliseconds(Environment.TickCount64) + options.TransportSendTimeout + TimeSpan.FromTicks(1));
1214+
Assert.True(connection.SendingToken.IsCancellationRequested);
1215+
12001216
sync.Continue();
12011217
await dispatcherTask.DefaultTimeout();
12021218
// Connection should be removed on canceled write
@@ -1215,6 +1231,7 @@ bool ExpectedErrors(WriteContext writeContext)
12151231
}
12161232
using (StartVerifiableLog(expectedErrorsFilter: ExpectedErrors))
12171233
{
1234+
var initialTime = TimeSpan.FromMilliseconds(Environment.TickCount64);
12181235
var manager = CreateConnectionManager(LoggerFactory);
12191236
var connection = manager.CreateConnection();
12201237
connection.TransportType = HttpTransportType.WebSockets;
@@ -1232,8 +1249,15 @@ bool ExpectedErrors(WriteContext writeContext)
12321249
var dispatcherTask = dispatcher.ExecuteAsync(context, options, app);
12331250
await connection.Transport.Output.WriteAsync(new byte[] { 1 }).DefaultTimeout();
12341251
await sync.WaitForSyncPoint().DefaultTimeout();
1252+
1253+
// Try cancel before cancellation should occur
1254+
connection.TryCancelSend(initialTime + options.TransportSendTimeout);
1255+
Assert.False(connection.SendingToken.IsCancellationRequested);
1256+
12351257
// Cancel write to response body
1236-
connection.TryCancelSend(long.MaxValue);
1258+
connection.TryCancelSend(TimeSpan.FromMilliseconds(Environment.TickCount64) + options.TransportSendTimeout + TimeSpan.FromTicks(1));
1259+
Assert.True(connection.SendingToken.IsCancellationRequested);
1260+
12371261
sync.Continue();
12381262
await dispatcherTask.DefaultTimeout();
12391263
// Connection should be removed on canceled write

0 commit comments

Comments
 (0)