From 1f5cf3115a310ccaa027a067b99f7b6ee27d2a4f Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 2 Nov 2016 12:46:30 -0400 Subject: [PATCH 1/3] Remove unused BufferAsyncResult.IsWrite field --- .../src/System/Net/BufferAsyncResult.cs | 7 ------- .../src/System/Net/Security/NegotiateStream.cs | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs b/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs index ce8ea0d8d0d6..6bcd460b154d 100644 --- a/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs +++ b/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs @@ -13,20 +13,13 @@ internal class BufferAsyncResult : LazyAsyncResult public byte[] Buffer; public int Offset; public int Count; - public bool IsWrite; public BufferAsyncResult(object asyncObject, byte[] buffer, int offset, int count, object asyncState, AsyncCallback asyncCallback) - : this(asyncObject, buffer, offset, count, false, asyncState, asyncCallback) - { - } - - public BufferAsyncResult(object asyncObject, byte[] buffer, int offset, int count, bool isWrite, object asyncState, AsyncCallback asyncCallback) : base(asyncObject, asyncState, asyncCallback) { Buffer = buffer; Offset = offset; Count = count; - IsWrite = isWrite; } } } diff --git a/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs b/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs index 9684f8761349..4079cd652798 100644 --- a/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs +++ b/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs @@ -645,7 +645,7 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As return InnerStream.BeginWrite(buffer, offset, count, asyncCallback, asyncState); } - BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, true, asyncState, asyncCallback); + BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback); AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult); ProcessWrite(buffer, offset, count, asyncRequest); From 411c1562c39e7965b78a6c21ce05660c9494a847 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 2 Nov 2016 13:07:31 -0400 Subject: [PATCH 2/3] Reuse AsyncProtocolRequest instances across read/write operations Saves an allocation per read and write, ammortizing the costs across the whole stream's lifetime. --- .../src/System/Net/HelperAsyncResults.cs | 26 +++++++++- .../System/Net/Security/SslStreamInternal.cs | 47 +++++++++++++++---- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs b/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs index c6f090c90c99..af352488dd68 100644 --- a/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs +++ b/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Threading; namespace System.Net @@ -31,7 +30,6 @@ internal class AsyncProtocolRequest private const int StatusCompleted = 1; private const int StatusCheckedOnSyncCompletion = 2; - public LazyAsyncResult UserAsyncResult; public int Result; public object AsyncState; @@ -53,6 +51,30 @@ public AsyncProtocolRequest(LazyAsyncResult userAsyncResult) UserAsyncResult = userAsyncResult; } + public void Reset(LazyAsyncResult userAsyncResult) + { + if (userAsyncResult == null) + { + NetEventSource.Fail(this, "userAsyncResult == null"); + } + if (userAsyncResult.InternalPeekCompleted) + { + NetEventSource.Fail(this, "userAsyncResult is already completed."); + } + UserAsyncResult = userAsyncResult; + + _callback = null; + _completionStatus = 0; + Result = 0; + AsyncState = null; + Buffer = null; + Offset = 0; + Count = 0; +#if DEBUG + _DebugAsyncChain = 0; +#endif + } + public void SetNextRequest(byte[] buffer, int offset, int count, AsyncProtocolCallback callback) { if (_completionStatus != StatusNotStarted) diff --git a/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs b/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs index 118e5706b654..f04da135bb0a 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs @@ -27,6 +27,8 @@ internal class SslStreamInternal private SslState _sslState; private int _nestedWrite; private int _nestedRead; + private AsyncProtocolRequest _readProtocolRequest; // cached, reusable AsyncProtocolRequest used for read operations + private AsyncProtocolRequest _writeProtocolRequest; // cached, reusable AsyncProtocolRequest used for write operations // Never updated directly, special properties are used. This is the read buffer. private byte[] _internalBuffer; @@ -131,9 +133,8 @@ internal void Write(byte[] buffer, int offset, int count) internal IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) { - BufferAsyncResult bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback); - AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(bufferResult); - ProcessRead(buffer, offset, count, asyncRequest); + var bufferResult = new BufferAsyncResult(this, buffer, offset, count, asyncState, asyncCallback); + ProcessRead(buffer, offset, count, bufferResult); return bufferResult; } @@ -173,9 +174,8 @@ internal int EndRead(IAsyncResult asyncResult) internal IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) { - LazyAsyncResult lazyResult = new LazyAsyncResult(this, asyncState, asyncCallback); - AsyncProtocolRequest asyncRequest = new AsyncProtocolRequest(lazyResult); - ProcessWrite(buffer, offset, count, asyncRequest); + var lazyResult = new LazyAsyncResult(this, asyncState, asyncCallback); + ProcessWrite(buffer, offset, count, lazyResult); return lazyResult; } @@ -319,10 +319,31 @@ private void ValidateParameters(byte[] buffer, int offset, int count) } } + private AsyncProtocolRequest GetOrCreateProtocolRequest(ref AsyncProtocolRequest aprField, LazyAsyncResult asyncResult) + { + AsyncProtocolRequest request = null; + if (asyncResult != null) + { + // SslStreamInternal supports only a single read and a single write operation at a time. + // As such, we can cache and reuse the AsyncProtocolRequest object that's used throughout + // the implementation. + request = aprField; + if (request != null) + { + request.Reset(asyncResult); + } + else + { + aprField = request = new AsyncProtocolRequest(asyncResult); + } + } + return request; + } + // // Sync write method. // - private void ProcessWrite(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest) + private void ProcessWrite(byte[] buffer, int offset, int count, LazyAsyncResult asyncResult) { _sslState.CheckThrow(authSuccessCheck:true, shutdownCheck:true); ValidateParameters(buffer, offset, count); @@ -332,6 +353,10 @@ private void ProcessWrite(byte[] buffer, int offset, int count, AsyncProtocolReq throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, "Write", "write")); } + // If this is an async operation, get the AsyncProtocolRequest to use. + // We do this only after we verify we're the sole write operation in flight. + AsyncProtocolRequest asyncRequest = GetOrCreateProtocolRequest(ref _writeProtocolRequest, asyncResult); + bool failed = false; try @@ -470,15 +495,19 @@ private void StartWriting(byte[] buffer, int offset, int count, AsyncProtocolReq // // Combined sync/async read method. For sync request asyncRequest==null. // - private int ProcessRead(byte[] buffer, int offset, int count, AsyncProtocolRequest asyncRequest) + private int ProcessRead(byte[] buffer, int offset, int count, LazyAsyncResult asyncResult) { ValidateParameters(buffer, offset, count); if (Interlocked.Exchange(ref _nestedRead, 1) == 1) { - throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, (asyncRequest!=null? "BeginRead":"Read"), "read")); + throw new NotSupportedException(SR.Format(SR.net_io_invalidnestedcall, (asyncResult!=null? "BeginRead":"Read"), "read")); } + // If this is an async operation, get the AsyncProtocolRequest to use. + // We do this only after we verify we're the sole write operation in flight. + AsyncProtocolRequest asyncRequest = GetOrCreateProtocolRequest(ref _readProtocolRequest, asyncResult); + bool failed = false; try From 07b48256259838b6abb67cd43418f0cc816342e5 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 2 Nov 2016 14:24:10 -0400 Subject: [PATCH 3/3] Avoid boxing Int32 result per operation - Change BufferAsyncResult to allow storing int result, without adding another field - Use that in both SslStream and NegotiateStream to avoid boxing an int per read and write - Also rename AsyncProtocolRequest.CompleteWithError to CompleteUserWithError, to avoid confusion and keep it consistent with the other CompleteUser methods. --- .../src/System/Net/BufferAsyncResult.cs | 50 +++++++++++++++++-- .../src/System/Net/FixedSizeReader.cs | 2 +- .../src/System/Net/HelperAsyncResults.cs | 31 ++++-------- .../Net/Security/InternalNegotiateStream.cs | 19 ++----- .../System/Net/Security/NegotiateStream.cs | 2 +- .../src/System/Net/Security/SslState.cs | 4 +- .../System/Net/Security/SslStreamInternal.cs | 38 +++++--------- 7 files changed, 76 insertions(+), 70 deletions(-) diff --git a/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs b/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs index 6bcd460b154d..b5d4d530f5cf 100644 --- a/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs +++ b/src/System.Net.Security/src/System/Net/BufferAsyncResult.cs @@ -2,24 +2,64 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; + namespace System.Net { // // Preserve the original request buffer & sizes for user IO requests. // This is returned as an IAsyncResult to the application. // - internal class BufferAsyncResult : LazyAsyncResult + internal sealed class BufferAsyncResult : LazyAsyncResult { - public byte[] Buffer; - public int Offset; - public int Count; + /// Stored into LazyAsyncResult.Result to indicate a completed result. + public static readonly object ResultSentinal = nameof(BufferAsyncResult) + "." + nameof(ResultSentinal); + /// Stores the input count or the output result of the operation. + private int _countOrResult; +#if DEBUG + /// true if backs , false if . + private bool _countOrResultIsResult; +#endif public BufferAsyncResult(object asyncObject, byte[] buffer, int offset, int count, object asyncState, AsyncCallback asyncCallback) : base(asyncObject, asyncState, asyncCallback) { Buffer = buffer; Offset = offset; - Count = count; + _countOrResult = count; + } + + public byte[] Buffer { get; } + public int Offset { get; } + public int Count + { + get + { +#if DEBUG + Debug.Assert(!_countOrResultIsResult, "Trying to get count after it's already the result"); +#endif + return _countOrResult; + } + } + + public int Int32Result // Int32Result to differentiate from the base's "object Result" + { + get + { +#if DEBUG + Debug.Assert(_countOrResultIsResult, "Still represents the count, not the result"); + Debug.Assert(ReferenceEquals(Result, ResultSentinal), "Expected the base object Result to be the sentinel"); +#endif + return _countOrResult; + } + set + { +#if DEBUG + Debug.Assert(!_countOrResultIsResult, "Should only be set when result hasn't yet been set"); + _countOrResultIsResult = true; +#endif + _countOrResult = value; + } } } } diff --git a/src/System.Net.Security/src/System/Net/FixedSizeReader.cs b/src/System.Net.Security/src/System/Net/FixedSizeReader.cs index 85bae05d6743..c4eb5cb05f93 100644 --- a/src/System.Net.Security/src/System/Net/FixedSizeReader.cs +++ b/src/System.Net.Security/src/System/Net/FixedSizeReader.cs @@ -151,7 +151,7 @@ private static void ReadCallback(IAsyncResult transportResult) throw; } - request.CompleteWithError(e); + request.CompleteUserWithError(e); } } } diff --git a/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs b/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs index af352488dd68..736bd98e1b35 100644 --- a/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs +++ b/src/System.Net.Security/src/System/Net/HelperAsyncResults.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics; using System.Threading; namespace System.Net @@ -88,13 +89,7 @@ public void SetNextRequest(byte[] buffer, int offset, int count, AsyncProtocolCa _callback = callback; } - internal object AsyncObject - { - get - { - return UserAsyncResult.AsyncObject; - } - } + internal object AsyncObject => UserAsyncResult.AsyncObject; // // Notify protocol so a next stage could be started. @@ -137,24 +132,18 @@ public bool MustCompleteSynchronously // // Important: This will abandon _Callback and directly notify UserAsyncResult. // - internal void CompleteWithError(Exception e) - { - UserAsyncResult.InvokeCallback(e); - } + internal void CompleteUserWithError(Exception e) => UserAsyncResult.InvokeCallback(e); - internal void CompleteUser() - { - UserAsyncResult.InvokeCallback(); - } + internal void CompleteUser() => UserAsyncResult.InvokeCallback(); - internal void CompleteUser(object userResult) + internal void CompleteUser(int userResult) { - UserAsyncResult.InvokeCallback(userResult); + Debug.Assert(UserAsyncResult is BufferAsyncResult, "CompleteUser(int) may only be used with a BufferAsyncResult"); + var bar = (BufferAsyncResult)UserAsyncResult; + bar.Int32Result = userResult; + bar.InvokeCallback(BufferAsyncResult.ResultSentinal); } - internal bool IsUserCompleted - { - get { return UserAsyncResult.InternalPeekCompleted; } - } + internal bool IsUserCompleted => UserAsyncResult.InternalPeekCompleted; } } diff --git a/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs b/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs index 26f756e9984d..c57008bb9c47 100644 --- a/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs +++ b/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs @@ -216,10 +216,7 @@ private int ProcessRead(byte[] buffer, int offset, int count, AsyncProtocolReque Buffer.BlockCopy(InternalBuffer, InternalOffset, buffer, offset, copyBytes); DecrementInternalBufferCount(copyBytes); } - if (asyncRequest != null) - { - asyncRequest.CompleteUser((object)copyBytes); - } + asyncRequest?.CompleteUser(copyBytes); return copyBytes; } @@ -283,10 +280,7 @@ private int StartFrameBody(int readBytes, byte[] buffer, int offset, int count, if (readBytes == 0) { //EOF - if (asyncRequest != null) - { - asyncRequest.CompleteUser((object)0); - } + asyncRequest?.CompleteUser(0); return 0; } @@ -368,10 +362,7 @@ private int ProcessFrameBody(int readBytes, byte[] buffer, int offset, int count // This will adjust both the remaining internal buffer count and the offset. DecrementInternalBufferCount(readBytes); - if (asyncRequest != null) - { - asyncRequest.CompleteUser((object)readBytes); - } + asyncRequest?.CompleteUser(readBytes); return readBytes; } @@ -410,7 +401,7 @@ private static void WriteCallback(IAsyncResult transportResult) throw; } - asyncRequest.CompleteWithError(e); + asyncRequest.CompleteUserWithError(e); } } @@ -444,7 +435,7 @@ private static void ReadCallback(AsyncProtocolRequest asyncRequest) throw; } - asyncRequest.CompleteWithError(e); + asyncRequest.CompleteUserWithError(e); } } } diff --git a/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs b/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs index 4079cd652798..fff772ee9fb5 100644 --- a/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs +++ b/src/System.Net.Security/src/System/Net/Security/NegotiateStream.cs @@ -625,7 +625,7 @@ public override int EndRead(IAsyncResult asyncResult) throw new IOException(SR.net_io_read, (Exception)bufferResult.Result); } - return (int)bufferResult.Result; + return bufferResult.Int32Result; #if DEBUG } #endif diff --git a/src/System.Net.Security/src/System/Net/Security/SslState.cs b/src/System.Net.Security/src/System/Net/Security/SslState.cs index e227a0f19ab6..6e4ea087cf22 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslState.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslState.cs @@ -1441,7 +1441,7 @@ private void FinishHandshake(Exception e, AsyncProtocolRequest asyncRequest) { if (e != null) { - asyncRequest.CompleteWithError(e); + asyncRequest.CompleteUserWithError(e); } else { @@ -1725,7 +1725,7 @@ private void AsyncResumeHandshake(object state) } catch (Exception e) { - request.CompleteWithError(e); + request.CompleteUserWithError(e); } } diff --git a/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs b/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs index f04da135bb0a..c7a6d7ff705d 100644 --- a/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs +++ b/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs @@ -169,7 +169,7 @@ internal int EndRead(IAsyncResult asyncResult) throw new IOException(SR.net_io_read, (Exception)bufferResult.Result); } - return (int)bufferResult.Result; + return bufferResult.Int32Result; } internal IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback asyncCallback, object asyncState) @@ -495,7 +495,7 @@ private void StartWriting(byte[] buffer, int offset, int count, AsyncProtocolReq // // Combined sync/async read method. For sync request asyncRequest==null. // - private int ProcessRead(byte[] buffer, int offset, int count, LazyAsyncResult asyncResult) + private int ProcessRead(byte[] buffer, int offset, int count, BufferAsyncResult asyncResult) { ValidateParameters(buffer, offset, count); @@ -522,9 +522,7 @@ private int ProcessRead(byte[] buffer, int offset, int count, LazyAsyncResult as SkipBytes(copyBytes); } - if (asyncRequest != null) { - asyncRequest.CompleteUser((object) copyBytes); - } + asyncRequest?.CompleteUser(copyBytes); return copyBytes; } @@ -580,10 +578,7 @@ private int StartReading(byte[] buffer, int offset, int count, AsyncProtocolRequ if (copyBytes != -1) { - if (asyncRequest != null) - { - asyncRequest.CompleteUser((object)copyBytes); - } + asyncRequest?.CompleteUser(copyBytes); return copyBytes; } @@ -633,10 +628,7 @@ private int StartFrameBody(int readBytes, byte[] buffer, int offset, int count, { //EOF : Reset the buffer as we did not read anything into it. SkipBytes(InternalBufferCount); - if (asyncRequest != null) - { - asyncRequest.CompleteUser((object)0); - } + asyncRequest?.CompleteUser(0); return 0; } @@ -728,10 +720,7 @@ private int ProcessFrameBody(int readBytes, byte[] buffer, int offset, int count SkipBytes(readBytes); _sslState.FinishRead(null); - if (asyncRequest != null) - { - asyncRequest.CompleteUser((object)readBytes); - } + asyncRequest?.CompleteUser(readBytes); return readBytes; } @@ -752,10 +741,7 @@ private int ProcessReadErrorCode(SecurityStatusPal status, byte[] buffer, int of if (message.CloseConnection) { _sslState.FinishRead(null); - if (asyncRequest != null) - { - asyncRequest.CompleteUser((object)0); - } + asyncRequest?.CompleteUser(0); return 0; } @@ -801,7 +787,7 @@ private static void WriteCallback(IAsyncResult transportResult) } sslStream._sslState.FinishWrite(); - asyncRequest.CompleteWithError(e); + asyncRequest.CompleteUserWithError(e); } } @@ -823,7 +809,7 @@ private static void ResumeAsyncReadCallback(AsyncProtocolRequest request) } ((SslStreamInternal)request.AsyncObject)._sslState.FinishRead(null); - request.CompleteWithError(e); + request.CompleteUserWithError(e); } } @@ -845,7 +831,7 @@ private static void ResumeAsyncWriteCallback(AsyncProtocolRequest asyncRequest) } ((SslStreamInternal)asyncRequest.AsyncObject)._sslState.FinishWrite(); - asyncRequest.CompleteWithError(e); + asyncRequest.CompleteUserWithError(e); } } @@ -869,7 +855,7 @@ private static void ReadHeaderCallback(AsyncProtocolRequest asyncRequest) throw; } - asyncRequest.CompleteWithError(e); + asyncRequest.CompleteUserWithError(e); } } @@ -893,7 +879,7 @@ private static void ReadFrameCallback(AsyncProtocolRequest asyncRequest) throw; } - asyncRequest.CompleteWithError(e); + asyncRequest.CompleteUserWithError(e); } } }