internal void CheckResetConnection(TdsParserStateObject stateObj) { if (_fResetConnection && !stateObj._fResetConnectionSent) { Debug.Assert(stateObj._outputPacketNumber == 1 || stateObj._outputPacketNumber == 2, "In ResetConnection logic unexpectedly!"); try { if (_fMARS && !stateObj._fResetEventOwned) { // If using Async & MARS and we do not own ResetEvent - grab it. We need to not grab lock here // for case where multiple packets are sent to server from one execute. stateObj._fResetEventOwned = _resetConnectionEvent.WaitOne(stateObj.GetTimeoutRemaining()); if (stateObj._fResetEventOwned) { if (stateObj.TimeoutHasExpired) { // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining. stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); stateObj.TimeoutTime = 0; } } if (!stateObj._fResetEventOwned) { // We timed out waiting for ResetEvent. Throw timeout exception and reset // the buffer. Nothing else to do since we did not actually send anything // to the server. stateObj.ResetBuffer(); Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point."); stateObj.AddError(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, _server, _connHandler.TimeoutErrorInternal.GetErrorMessage(), "", 0, TdsEnums.SNI_WAIT_TIMEOUT)); Debug.Assert(_connHandler._parserLock.ThreadMayHaveLock(), "Thread is writing without taking the connection lock"); ThrowExceptionAndWarning(stateObj, callerHasConnectionLock: true); } } if (_fResetConnection) { // Check again to see if we need to send reset. Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state for sending reset connection"); { // if we are reseting, set bit in header by or'ing with other value stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION); } if (!_fMARS) { _fResetConnection = false; // If not MARS, can turn off flag now. } else { stateObj._fResetConnectionSent = true; // Otherwise set flag so we don't resend on multiple packet execute. } } else if (_fMARS && stateObj._fResetEventOwned) { Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state on WritePacket ResetConnection"); // Otherwise if Yukon and we grabbed the event, free it. Another execute grabbed the event and // took care of sending the reset. stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); } } catch (Exception) { if (_fMARS && stateObj._fResetEventOwned) { // If exception thrown, and we are on Yukon and own the event, release it! stateObj._fResetConnectionSent = false; stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); } throw; } } #if DEBUG else { Debug.Assert(!_fResetConnection || (_fResetConnection && stateObj._fResetConnectionSent && stateObj._fResetEventOwned), "Unexpected state on else ResetConnection block in WritePacket"); } #endif }
// This method should only be called by ReadSni! If not - it may have problems with timeouts! private void ReadSniError(TdsParserStateObject stateObj, UInt32 error) { if (TdsEnums.SNI_WAIT_TIMEOUT == error) { Debug.Assert(_syncOverAsync, "Should never reach here with async on!"); bool fail = false; if (_internalTimeout) { // This is now our second timeout - time to give up. fail = true; } else { stateObj._internalTimeout = true; Debug.Assert(_parser.Connection != null, "SqlConnectionInternalTds handler can not be null at this point."); AddError(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, _parser.Server, _parser.Connection.TimeoutErrorInternal.GetErrorMessage(), "", 0, TdsEnums.SNI_WAIT_TIMEOUT)); if (!stateObj._attentionSent) { if (stateObj.Parser.State == TdsParserState.OpenLoggedIn) { stateObj.SendAttention(mustTakeWriteLock: true); IntPtr syncReadPacket = IntPtr.Zero; bool shouldDecrement = false; try { Interlocked.Increment(ref _readingCount); shouldDecrement = true; SNIHandle handle = Handle; if (handle == null) { throw ADP.ClosedConnectionError(); } error = SNINativeMethodWrapper.SNIReadSyncOverAsync(handle, ref syncReadPacket, stateObj.GetTimeoutRemaining()); Interlocked.Decrement(ref _readingCount); shouldDecrement = false; if (TdsEnums.SNI_SUCCESS == error) { // We will end up letting the run method deal with the expected done:done_attn token stream. stateObj.ProcessSniPacket(syncReadPacket, 0); return; } else { Debug.Assert(IntPtr.Zero == syncReadPacket, "unexpected syncReadPacket without corresponding SNIPacketRelease"); fail = true; // Subsequent read failed, time to give up. } } finally { if (shouldDecrement) { Interlocked.Decrement(ref _readingCount); } if (syncReadPacket != IntPtr.Zero) { // Be sure to release packet, otherwise it will be leaked by native. SNINativeMethodWrapper.SNIPacketRelease(syncReadPacket); } } } else { if (_parser._loginWithFailover) { // For DbMirroring Failover during login, never break the connection, just close the TdsParser _parser.Disconnect(); } else if ((_parser.State == TdsParserState.OpenNotLoggedIn) && (_parser.Connection.ConnectionOptions.MultiSubnetFailover)) { // For MultiSubnet Failover during login, never break the connection, just close the TdsParser _parser.Disconnect(); } else fail = true; // We aren't yet logged in - just fail. } } } if (fail) { _parser.State = TdsParserState.Broken; // We failed subsequent read, we have to quit! _parser.Connection.BreakConnection(); } } else { // Caution: ProcessSNIError always returns a fatal error! AddError(_parser.ProcessSNIError(stateObj)); } ThrowExceptionAndWarning(); AssertValidState(); }