-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
decrease allocations during ssl handshake #1350
Conversation
@@ -300,6 +300,7 @@ internal static bool DoSslHandshake(SafeSslHandle context, ReadOnlySpan<byte> in | |||
{ | |||
if (sendCount <= 0) | |||
{ | |||
ArrayPool<byte>.Shared.Return(sendBuf); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd feel ever so slightly better if this were instead:
byte[] tmp = sendBuf;
sendBuf = null;
ArrayPool<byte>.Shared.Return(tmp);
If, for example, Crypto.ErrClearError were to throw an exception, with the code as written we could have returned the buffer to the pool but the caller could still have a reference via the out argument that was already assigned.
@@ -718,35 +719,45 @@ private bool AcquireServerCredentials(ref byte[] thumbPrint, ReadOnlySpan<byte> | |||
} | |||
|
|||
// | |||
internal ProtocolToken NextMessage(byte[] incoming, int offset, int count) | |||
internal ProtocolToken NextMessage(byte[] incoming, int offset, int count, ProtocolToken message = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just make ProtocolToken a readonly struct?
{ | ||
if (SslStreamPal.RentsBufferForHanshake && _payload != null) | ||
{ | ||
ArrayPool<byte>.Shared.Return(_payload); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh, yeah, this definitely makes me nervous. Erroneous setting of Payload could now result in the same buffer being returned multiple times to the pool, e.g. if we accidentally access it concurrently. And it'll be worse if we do make ProtocolToken a struct (maybe that's why you chose not to).
Does _payload need to actually be a separate buffer? We already have _internalBuffer on the SslStream... can we just use that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering about it but the ProtocolToken use is currently pretty simple and controlled by us. I originally did not have it IDisposable and I was worrying about loosing rented buffer in error cases and causing memory leaks. I call Reset() when we are done with token to release buffer as soon as possible. We can possibly assert or throw if buffer is not null when assigning new value. That would make sure we don't mingle usages.
ProtocolToken is IMHO convenience wrapper and we could possibly abandon it and track status and buffers separately. That would make it easier to track life cycle.
I was also thinking about using _internalBuffer. Handshake happens before we would normally use it and we rent buffer big enough for whole TLS record. The trouble would be handling renegotiation. It still may be possible but the _internalBuffer could have chunk of data while we would start new handshake. We could use same strategy on separate variable and keep the rented buffer in class variable instead of holding to it via ProtocolToken.
Let me know what seems most appropriate.
I'll find another solution and I'll open new PR when ready. |
The test I'm using is essentially handshake test from Performance repo with one round of setup and 1000 test iterations:
When running with OpenSSL we get following allocations:
There are two parts in this change:
ProtocolToken is container to hold status and buffer. It can be allocated once and reused. That brings 5 allocation to 1. We may possibly convert it to struct in future.
We already use shared buffers for encryption.decryption. Wit this change we will also use it for outgoing handshake messages when openssl is used.