From 77f69928c4cfb72ddd089662c6af66254e0da3f9 Mon Sep 17 00:00:00 2001 From: panoskj Date: Mon, 25 Sep 2023 16:49:13 +0300 Subject: [PATCH] 1. Merging "Buffer read" methods of TdsParserStateObject, port #667 to netfx. Replaced Thread.MemoryBarrier usage of netfx with Interlocked.MemoryBarrier. --- .../SqlClient/TdsParserStateObject.netcore.cs | 164 ----------------- .../SqlClient/TdsParserStateObject.netfx.cs | 164 ----------------- .../Data/SqlClient/TdsParserStateObject.cs | 169 ++++++++++++++++++ 3 files changed, 169 insertions(+), 328 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs index cb3e547418..1a968aad51 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs @@ -282,170 +282,6 @@ internal void StartSession(object cancellationOwner) // Buffer read methods - data values // /////////////////////////////////////// - // look at the next byte without pulling it off the wire, don't just return _inBytesUsed since we may - // have to go to the network to get the next byte. - internal bool TryPeekByte(out byte value) - { - if (!TryReadByte(out value)) - { - return false; - } - - // now do fixup - _inBytesPacket++; - _inBytesUsed--; - - AssertValidState(); - return true; - } - - // Takes a byte array, an offset, and a len and fills the array from the offset to len number of - // bytes from the in buffer. - public bool TryReadByteArray(Span buff, int len) - { - return TryReadByteArray(buff, len, out _); - } - - // NOTE: This method must be retriable WITHOUT replaying a snapshot - // Every time you call this method increment the offset and decrease len by the value of totalRead - public bool TryReadByteArray(Span buff, int len, out int totalRead) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method -#endif - totalRead = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); - - // loop through and read up to array length - while (len > 0) - { - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); - Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); - if (!buff.IsEmpty) - { - ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); - Span copyTo = buff.Slice(totalRead, bytesToRead); - copyFrom.CopyTo(copyTo); - } - - totalRead += bytesToRead; - _inBytesUsed += bytesToRead; - _inBytesPacket -= bytesToRead; - len -= bytesToRead; - - AssertValidState(); - } - - return true; - } - - // Takes no arguments and returns a byte from the buffer. If the buffer is empty, it is filled - // before the byte is returned. - internal bool TryReadByte(out byte value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); - value = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - // decrement the number of bytes left in the packet - _inBytesPacket--; - - Debug.Assert(_inBytesPacket >= 0, "ERROR - TDSParser: _inBytesPacket < 0"); - - // return the byte from the buffer and increment the counter for number of bytes used in the in buffer - value = (_inBuff[_inBytesUsed++]); - - AssertValidState(); - return true; - } - - internal bool TryReadChar(out char value) - { - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - - Span buffer = stackalloc byte[2]; - if (((_inBytesUsed + 2) > _inBytesRead) || (_inBytesPacket < 2)) - { - // If the char isn't fully in the buffer, or if it isn't fully in the packet, - // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(buffer, 2)) - { - value = '\0'; - return false; - } - } - else - { - // The entire char is in the packet and in the buffer, so just return it - // and take care of the counters. - buffer = _inBuff.AsSpan(_inBytesUsed, 2); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (char)((buffer[1] << 8) + buffer[0]); - - return true; - } - internal bool TryReadInt16(out short value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs index 4baf19657a..65e86f23ea 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs @@ -376,170 +376,6 @@ internal void StartSession(int objectID) // Buffer read methods - data values // /////////////////////////////////////// - // look at the next byte without pulling it off the wire, don't just return _inBytesUsed since we may - // have to go to the network to get the next byte. - internal bool TryPeekByte(out byte value) - { - if (!TryReadByte(out value)) - { - return false; - } - - // now do fixup - _inBytesPacket++; - _inBytesUsed--; - - AssertValidState(); - return true; - } - - // Takes a byte array, an offset, and a len and fills the array from the offset to len number of - // bytes from the in buffer. - public bool TryReadByteArray(Span buff, int len) - { - return TryReadByteArray(buff, len, out _); - } - - // NOTE: This method must be retriable WITHOUT replaying a snapshot - // Every time you call this method increment the offset and decrease len by the value of totalRead - public bool TryReadByteArray(Span buff, int len, out int totalRead) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method -#endif - totalRead = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); - - // loop through and read up to array length - while (len > 0) - { - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); - Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); - if (!buff.IsEmpty) - { - ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); - Span copyTo = buff.Slice(totalRead, bytesToRead); - copyFrom.CopyTo(copyTo); - } - - totalRead += bytesToRead; - _inBytesUsed += bytesToRead; - _inBytesPacket -= bytesToRead; - len -= bytesToRead; - - AssertValidState(); - } - - return true; - } - - // Takes no arguments and returns a byte from the buffer. If the buffer is empty, it is filled - // before the byte is returned. - internal bool TryReadByte(out byte value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); - value = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - // decrement the number of bytes left in the packet - _inBytesPacket--; - - Debug.Assert(_inBytesPacket >= 0, "ERROR - TDSParser: _inBytesPacket < 0"); - - // return the byte from the buffer and increment the counter for number of bytes used in the in buffer - value = (_inBuff[_inBytesUsed++]); - - AssertValidState(); - return true; - } - - internal bool TryReadChar(out char value) - { - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - - Span buffer = stackalloc byte[2]; - if (((_inBytesUsed + 2) > _inBytesRead) || (_inBytesPacket < 2)) - { - // If the char isn't fully in the buffer, or if it isn't fully in the packet, - // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(buffer, 2)) - { - value = '\0'; - return false; - } - } - else - { - // The entire char is in the packet and in the buffer, so just return it - // and take care of the counters. - buffer = _inBuff.AsSpan(_inBytesUsed, 2); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (char)((buffer[1] << 8) + buffer[0]); - - return true; - } - internal bool TryReadInt16(out short value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 4c764a2bfa..acae24ec0f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1113,6 +1113,175 @@ internal bool SetPacketSize(int size) return false; } + + /////////////////////////////////////// + // Buffer read methods - data values // + /////////////////////////////////////// + + // look at the next byte without pulling it off the wire, don't just return _inBytesUsed since we may + // have to go to the network to get the next byte. + internal bool TryPeekByte(out byte value) + { + if (!TryReadByte(out value)) + { + return false; + } + + // now do fixup + _inBytesPacket++; + _inBytesUsed--; + + AssertValidState(); + return true; + } + + // Takes a byte array, an offset, and a len and fills the array from the offset to len number of + // bytes from the in buffer. + public bool TryReadByteArray(Span buff, int len) + { + return TryReadByteArray(buff, len, out _); + } + + // NOTE: This method must be retriable WITHOUT replaying a snapshot + // Every time you call this method increment the offset and decrease len by the value of totalRead + public bool TryReadByteArray(Span buff, int len, out int totalRead) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method +#endif + totalRead = 0; + +#if DEBUG + if (_snapshot != null && _snapshot.DoPend()) + { + _networkPacketTaskSource = new TaskCompletionSource(); + Interlocked.MemoryBarrier(); + + if (s_forcePendingReadsToWaitForUser) + { + _realNetworkPacketTaskSource = new TaskCompletionSource(); + _realNetworkPacketTaskSource.SetResult(null); + } + else + { + _networkPacketTaskSource.TrySetResult(null); + } + return false; + } +#endif + + Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); + + // loop through and read up to array length + while (len > 0) + { + if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) + { + if (!TryPrepareBuffer()) + { + return false; + } + } + + int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); + Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); + if (!buff.IsEmpty) + { + ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); + Span copyTo = buff.Slice(totalRead, bytesToRead); + copyFrom.CopyTo(copyTo); + } + + totalRead += bytesToRead; + _inBytesUsed += bytesToRead; + _inBytesPacket -= bytesToRead; + len -= bytesToRead; + + AssertValidState(); + } + + return true; + } + + // Takes no arguments and returns a byte from the buffer. If the buffer is empty, it is filled + // before the byte is returned. + internal bool TryReadByte(out byte value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method +#endif + Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); + value = 0; + +#if DEBUG + if (_snapshot != null && _snapshot.DoPend()) + { + _networkPacketTaskSource = new TaskCompletionSource(); + Interlocked.MemoryBarrier(); + + if (s_forcePendingReadsToWaitForUser) + { + _realNetworkPacketTaskSource = new TaskCompletionSource(); + _realNetworkPacketTaskSource.SetResult(null); + } + else + { + _networkPacketTaskSource.TrySetResult(null); + } + return false; + } +#endif + + if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) + { + if (!TryPrepareBuffer()) + { + return false; + } + } + + // decrement the number of bytes left in the packet + _inBytesPacket--; + + Debug.Assert(_inBytesPacket >= 0, "ERROR - TDSParser: _inBytesPacket < 0"); + + // return the byte from the buffer and increment the counter for number of bytes used in the in buffer + value = (_inBuff[_inBytesUsed++]); + + AssertValidState(); + return true; + } + + internal bool TryReadChar(out char value) + { + Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); + + Span buffer = stackalloc byte[2]; + if (((_inBytesUsed + 2) > _inBytesRead) || (_inBytesPacket < 2)) + { + // If the char isn't fully in the buffer, or if it isn't fully in the packet, + // then use ReadByteArray since the logic is there to take care of that. + if (!TryReadByteArray(buffer, 2)) + { + value = '\0'; + return false; + } + } + else + { + // The entire char is in the packet and in the buffer, so just return it + // and take care of the counters. + buffer = _inBuff.AsSpan(_inBytesUsed, 2); + _inBytesUsed += 2; + _inBytesPacket -= 2; + } + + AssertValidState(); + value = (char)((buffer[1] << 8) + buffer[0]); + + return true; + } + /* // leave this in. comes handy if you have to do Console.WriteLine style debugging ;)