Skip to content

Commit 5de9b7d

Browse files
authored
Re-use cancellation tokens in the https middleware (#31528)
* Re-use cancellation tokens in the https middleware - These tokens are usually short lived and exist for the purposes of cancelling the handshake.
1 parent 3143d95 commit 5de9b7d

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Concurrent;
5+
using System.Threading;
6+
7+
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
8+
{
9+
internal class CancellationTokenSourcePool
10+
{
11+
private const int MaxQueueSize = 1024;
12+
13+
private readonly ConcurrentQueue<PooledCancellationTokenSource> _queue = new();
14+
private int _count;
15+
16+
public PooledCancellationTokenSource Rent()
17+
{
18+
if (_queue.TryDequeue(out var cts))
19+
{
20+
Interlocked.Decrement(ref _count);
21+
return cts;
22+
}
23+
return new PooledCancellationTokenSource(this);
24+
}
25+
26+
private bool Return(PooledCancellationTokenSource cts)
27+
{
28+
if (Interlocked.Increment(ref _count) > MaxQueueSize || !cts.TryReset())
29+
{
30+
Interlocked.Decrement(ref _count);
31+
return false;
32+
}
33+
34+
_queue.Enqueue(cts);
35+
return true;
36+
}
37+
38+
/// <summary>
39+
/// A <see cref="CancellationTokenSource"/> with a back pointer to the pool it came from.
40+
/// Dispose will return it to the pool.
41+
/// </summary>
42+
public class PooledCancellationTokenSource : CancellationTokenSource
43+
{
44+
private readonly CancellationTokenSourcePool _pool;
45+
46+
public PooledCancellationTokenSource(CancellationTokenSourcePool pool)
47+
{
48+
_pool = pool;
49+
}
50+
51+
protected override void Dispose(bool disposing)
52+
{
53+
if (disposing)
54+
{
55+
// If we failed to return to the pool then dispose
56+
if (!_pool.Return(this))
57+
{
58+
base.Dispose(disposing);
59+
}
60+
}
61+
}
62+
}
63+
}
64+
}

src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ internal class HttpsConnectionMiddleware
4949
private readonly HttpsOptionsCallback? _httpsOptionsCallback;
5050
private readonly object? _httpsOptionsCallbackState;
5151

52+
// Pool for cancellation tokens that cancel the handshake
53+
private readonly CancellationTokenSourcePool _ctsPool = new();
54+
5255
public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options)
5356
: this(next, options, loggerFactory: NullLoggerFactory.Instance)
5457
{
@@ -150,7 +153,9 @@ public async Task OnConnectionAsync(ConnectionContext context)
150153

151154
try
152155
{
153-
using var cancellationTokenSource = new CancellationTokenSource(_handshakeTimeout);
156+
using var cancellationTokenSource = _ctsPool.Rent();
157+
cancellationTokenSource.CancelAfter(_handshakeTimeout);
158+
154159
if (_httpsOptionsCallback is null)
155160
{
156161
await DoOptionsBasedHandshakeAsync(context, sslStream, feature, cancellationTokenSource.Token);

0 commit comments

Comments
 (0)