public void WriteAsyncCallback()

in System.Data/System/Data/SqlClient/TdsParserStateObject.cs [2559:3745]


        public void WriteAsyncCallback(IntPtr key, IntPtr packet, UInt32 sniError) { // Key never used.
            RemovePacketFromPendingList(packet);
            try {
                if (sniError != TdsEnums.SNI_SUCCESS) {
                    Bid.Trace("<sc.TdsParser.WriteAsyncCallback|Info> write async returned error code %d\n", (int)sniError);
                    try {
                        AddError(_parser.ProcessSNIError(this));
                        ThrowExceptionAndWarning(asyncClose: true);
                    }
                    catch (Exception e) {
                        var writeCompletionSource = _writeCompletionSource;
                        if (writeCompletionSource != null) {
                            writeCompletionSource.TrySetException(e);
                        }
                        else {
                            _delayedWriteAsyncCallbackException = e;

                            // Ensure that _delayedWriteAsyncCallbackException is set before checking _writeCompletionSource
                            Thread.MemoryBarrier();
                            
                            // Double check that _writeCompletionSource hasn't been created in the meantime
                            writeCompletionSource = _writeCompletionSource;
                            if (writeCompletionSource != null) {
                                var delayedException = Interlocked.Exchange(ref _delayedWriteAsyncCallbackException, null);
                                if (delayedException != null) {
                                    writeCompletionSource.TrySetException(delayedException);
                                }
                            }              
                        }

                        return;
                    }
                }
                else {
                    _lastSuccessfulIOTimer._value = DateTime.UtcNow.Ticks;
                }
            }
            finally {
#if DEBUG
                if (SqlCommand.DebugForceAsyncWriteDelay > 0) {
                     new Timer(obj => {
                        Interlocked.Decrement(ref _asyncWriteCount);
                        var writeCompletionSource = _writeCompletionSource;
                        if (_asyncWriteCount == 0 && writeCompletionSource != null) {
                            writeCompletionSource.TrySetResult(null);
                        }
                    }, null, SqlCommand.DebugForceAsyncWriteDelay, Timeout.Infinite);
                }
                else {
#else
                {
#endif
                    Interlocked.Decrement(ref _asyncWriteCount);
                }
            }
#if DEBUG
            if (SqlCommand.DebugForceAsyncWriteDelay > 0) {
                return;
            }
#endif
            var completionSource = _writeCompletionSource;
            if (_asyncWriteCount == 0 && completionSource != null) {
                completionSource.TrySetResult(null);
            }           
        }

#pragma warning restore 420

        /////////////////////////////////////////
        // Network/Packet Writing & Processing //
        /////////////////////////////////////////

        
        //
        // Takes a secure string and offsets and saves them for a write latter when the information is written out to SNI Packet
        //  This method is provided to better handle the life cycle of the clear text of the secure string
        //  This method also ensures that the clear text is not held in the unpined managed buffer so that it avoids getting moved around by CLR garbage collector
        //  TdsParserStaticMethods.EncryptPassword operation is also done in the unmanaged buffer for the clear text later
        //
        internal void WriteSecureString(SecureString secureString) {
            TdsParser.ReliabilitySection.Assert("unreliable call to WriteSecureString");  // you need to setup for a thread abort somewhere before you call this method

            Debug.Assert(_securePasswords[0] == null || _securePasswords[1] == null, "There are more than two secure passwords");

            int index = _securePasswords[0] != null ? 1 : 0;

            _securePasswords[index] = secureString;
            _securePasswordOffsetsInBuffer[index] = _outBytesUsed;

            // loop through and write the entire array
            int lengthInBytes = secureString.Length * 2;

            // It is guaranteed both secure password and secure change password should fit into the first packet
            // Given current TDS format and implementation it is not possible that one of secure string is the last item and exactly fill up the output buffer
            //  if this ever happens and it is correct situation, the packet needs to be written out after _outBytesUsed is update
            Debug.Assert((_outBytesUsed + lengthInBytes) < _outBuff.Length, "Passwords cannot be splited into two different packet or the last item which fully fill up _outBuff!!!");

            _outBytesUsed += lengthInBytes;
        }

        // ResetSecurePasswordsInformation: clears information regarding secure passwords when login is done; called from TdsParser.TdsLogin
        internal void ResetSecurePasswordsInfomation() {
            for (int i = 0; i < _securePasswords.Length; ++i) {
                _securePasswords[i] = null;
                _securePasswordOffsetsInBuffer[i] = 0;
            }
        }
       
        internal Task WaitForAccumulatedWrites(  ) {
            // Checked for stored exceptions
#pragma warning disable 420 // A reference to a volatile field will not be treated as volatile - Disabling since the Interlocked APIs are volatile aware
            var delayedException = Interlocked.Exchange(ref _delayedWriteAsyncCallbackException, null);
            if (delayedException != null) {
                throw delayedException;
            }
#pragma warning restore 420 

            if (_asyncWriteCount == 0) {
                return null;
            }

            _writeCompletionSource = new TaskCompletionSource<object>();
            Task task = _writeCompletionSource.Task;

            // Ensure that _writeCompletionSource is set before checking state
            Thread.MemoryBarrier();

            // Now that we have set _writeCompletionSource, check if parser is closed or broken
            if  ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken)) {
                throw ADP.ClosedConnectionError();
            }

            // Check for stored exceptions
#pragma warning disable 420 // A reference to a volatile field will not be treated as volatile - Disabling since the Interlocked APIs are volatile aware
            delayedException = Interlocked.Exchange(ref _delayedWriteAsyncCallbackException, null);
            if (delayedException != null) {
                throw delayedException;
            }
#pragma warning restore 420 

            // If there are no outstanding writes, see if we can shortcut and return null
            if ((_asyncWriteCount == 0) && ((!task.IsCompleted) || (task.Exception == null))) {
                task = null;
            }

            return task;
        }

        // Takes in a single byte and writes it to the buffer.  If the buffer is full, it is flushed
        // and then the buffer is re-initialized in flush() and then the byte is put in the buffer.
        internal void WriteByte(byte b) {
            TdsParser.ReliabilitySection.Assert("unreliable call to WriteByte");  // you need to setup for a thread abort somewhere before you call this method

            Debug.Assert(_outBytesUsed <= _outBuff.Length, "ERROR - TDSParser: _outBytesUsed > _outBuff.Length");

            // check to make sure we haven't used the full amount of space available in the buffer, if so, flush it
            if (_outBytesUsed == _outBuff.Length) {
                WritePacket(TdsEnums.SOFTFLUSH, canAccumulate:true);
            }
            // set byte in buffer and increment the counter for number of bytes used in the out buffer
            _outBuff[_outBytesUsed++] = b;
        }
              
        //
        // Takes a byte array and writes it to the buffer.
        //
        internal Task WriteByteArray(Byte[] b, int len, int offsetBuffer, bool canAccumulate=true, TaskCompletionSource<object> completion = null) {
            try {
                TdsParser.ReliabilitySection.Assert("unreliable call to WriteByteArray");  // you need to setup for a thread abort somewhere before you call this method

                bool async = _parser._asyncWrite;  // NOTE: We are capturing this now for the assert after the Task is returned, since WritePacket will turn off async if there is an exception
                Debug.Assert(async || _asyncWriteCount == 0);
                // Do we have to send out in packet size chunks, or can we rely on netlib layer to break it up?
                // would prefer to to do something like:
                //
                // if (len > what we have room for || len > out buf)
                //   flush buffer
                //   UnsafeNativeMethods.Write(b)
                //
             
                int offset = offsetBuffer;

                Debug.Assert(b.Length >= len, "Invalid length sent to WriteByteArray()!");

                // loop through and write the entire array
                do {
                    if ((_outBytesUsed + len) > _outBuff.Length) {
                        // If the remainder of the string won't fit into the buffer, then we have to put
                        // whatever we can into the buffer, and flush that so we can then put more into
                        // the buffer on the next loop of the while.

                        int remainder = _outBuff.Length - _outBytesUsed;

                        // write the remainder
                        Buffer.BlockCopy(b, offset, _outBuff, _outBytesUsed, remainder);

                        // handle counters
                        offset += remainder;
                        _outBytesUsed += remainder;
                        len -= remainder;

                        Task packetTask = WritePacket(TdsEnums.SOFTFLUSH, canAccumulate);

                        if (packetTask != null) {
                            Task task = null;
                            Debug.Assert(async, "Returned task in sync mode");
                            if (completion == null) {
                                completion = new TaskCompletionSource<object>();
                                task = completion.Task; // we only care about return from topmost call, so do not access Task property in other cases
                            }
                            WriteByteArraySetupContinuation(b, len, completion, offset, packetTask);
                            return task;
                        }

                    }
                    else { //((stateObj._outBytesUsed + len) <= stateObj._outBuff.Length )
                        // Else the remainder of the string will fit into the buffer, so copy it into the
                        // buffer and then break out of the loop.

                        Buffer.BlockCopy(b, offset, _outBuff, _outBytesUsed, len);

                        // handle out buffer bytes used counter
                        _outBytesUsed += len;
                        break;
                    }
                } while (len > 0);

                if (completion != null) {
                    completion.SetResult(null);
                }
                return null;
            }
            catch (Exception e) {
                if (completion != null) {
                    completion.SetException(e);
                    return null;
                }
                else {
                    throw;
                }
            }
        }

        // This is in its own method to avoid always allocating the lambda in WriteByteArray
        private void WriteByteArraySetupContinuation(Byte[] b, int len, TaskCompletionSource<object> completion, int offset, Task packetTask) {
            AsyncHelper.ContinueTask(packetTask, completion,
                () => WriteByteArray(b, len: len, offsetBuffer: offset, canAccumulate: false, completion: completion),
                connectionToDoom: _parser.Connection);
        }

        // Dumps contents of buffer to SNI for network write.
        internal Task WritePacket(byte flushMode, bool canAccumulate = false) {
            if  ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken)) {
                throw ADP.ClosedConnectionError();
            }

            if (_parser.IsYukonOrNewer && !_bulkCopyOpperationInProgress // ignore the condition checking for bulk copy (SQL BU 414551)
                    && _outBytesUsed == (_outputHeaderLen + BitConverter.ToInt32(_outBuff, _outputHeaderLen))
                    && _outputPacketNumber == 1
                ||  _outBytesUsed == _outputHeaderLen
                    && _outputPacketNumber == 1) {
                return null;
            }

            byte status;
            byte packetNumber = _outputPacketNumber;

            // Set Status byte based whether this is end of message or not
            bool willCancel = (_cancelled) && (_parser._asyncWrite);
            if (willCancel) {
                status = TdsEnums.ST_EOM | TdsEnums.ST_IGNORE;
                _outputPacketNumber = 1;
            }
            else if (TdsEnums.HARDFLUSH == flushMode) {
                status = TdsEnums.ST_EOM;
                _outputPacketNumber = 1;              // end of message - reset to 1 - per ramas                
            }
            else if (TdsEnums.SOFTFLUSH==flushMode) {
                status = TdsEnums.ST_BATCH;
                _outputPacketNumber++;
            }
            else {
                status = TdsEnums.ST_EOM;
                Debug.Assert (false, String.Format((IFormatProvider)null, "Unexpected argument {0,-2:x2} to WritePacket", flushMode));
            }

            _outBuff[0] = _outputMessageType;         // Message Type
            _outBuff[1] = status;
            _outBuff[2] = (byte)(_outBytesUsed >> 8); // length - upper byte
            _outBuff[3] = (byte)(_outBytesUsed&0xff); // length - lower byte
            _outBuff[4] = 0;                          // channel
            _outBuff[5] = 0;
            _outBuff[6] = packetNumber;               // packet
            _outBuff[7] = 0;                          // window

            Task task=null;
            _parser.CheckResetConnection(this);       // HAS SIDE EFFECTS - re-org at a later time if possible

            task = WriteSni(canAccumulate);
            AssertValidState();

            if (willCancel) {
                // If we have been cancelled, then ensure that we write the ATTN packet as well
                task = AsyncHelper.CreateContinuationTask(task, CancelWritePacket, _parser.Connection);
            }

            return task;
        }

        private void CancelWritePacket() {
            Debug.Assert(_cancelled, "Should not call CancelWritePacket if _cancelled is not set");

            _parser.Connection.ThreadHasParserLockForClose = true;      // In case of error, let the connection know that we are holding the lock
            try {
                // Send the attention and wait for the ATTN_ACK
                SendAttention();
                ResetCancelAndProcessAttention();

                // Let the caller know that we've given up
                throw SQL.OperationCancelled();
            }
            finally {
                _parser.Connection.ThreadHasParserLockForClose = false;
            }
        }

#pragma warning disable 420 // a reference to a volatile field will not be treated as volatile

        private Task SNIWritePacket(SNIHandle handle, SNIPacket packet, out UInt32 sniError, bool canAccumulate, bool callerHasConnectionLock) {
            // Check for a stored exception
            var delayedException = Interlocked.Exchange(ref _delayedWriteAsyncCallbackException, null);
            if (delayedException != null) {
                throw delayedException;
            }

            Task task = null;
            _writeCompletionSource = null;
            IntPtr packetPointer = IntPtr.Zero;
            bool sync = !_parser._asyncWrite;
            if (sync && _asyncWriteCount > 0) { // for example, SendAttention while there are writes pending
                Task waitForWrites = WaitForAccumulatedWrites();
                if (waitForWrites != null) {
                    try {
                        waitForWrites.Wait();
                    }
                    catch (AggregateException ae) {
                        throw ae.InnerException;
                    }
                }
                Debug.Assert(_asyncWriteCount == 0, "All async write should be finished");
            }
            if (!sync) {
                // Add packet to the pending list (since the callback can happen any time after we call SNIWritePacket)
                packetPointer = AddPacketToPendingList(packet);
            }
            // Async operation completion may be delayed (success pending).
            RuntimeHelpers.PrepareConstrainedRegions();
            try {
            } 
            finally {
                sniError = SNINativeMethodWrapper.SNIWritePacket(handle, packet, sync);
            }
            if (sniError == TdsEnums.SNI_SUCCESS_IO_PENDING) {
                Debug.Assert(!sync, "Completion should be handled in SniManagedWwrapper");
                Interlocked.Increment(ref _asyncWriteCount);
                Debug.Assert(_asyncWriteCount >= 0);
                if (!canAccumulate) {
                    // Create completion source (for callback to complete)
                    _writeCompletionSource = new TaskCompletionSource<object>();
                    task = _writeCompletionSource.Task;

                    // Ensure that setting _writeCompletionSource completes before checking _delayedWriteAsyncCallbackException
                    Thread.MemoryBarrier();

                    // Check for a stored exception
                    delayedException = Interlocked.Exchange(ref _delayedWriteAsyncCallbackException, null);
                    if (delayedException != null) {
                        throw delayedException;
                    }

                    // If there are no outstanding writes, see if we can shortcut and return null
                    if ((_asyncWriteCount == 0) && ((!task.IsCompleted) || (task.Exception == null))) {
                        task = null;
                    }
                }
            }            
#if DEBUG
            else if (!sync && !canAccumulate && SqlCommand.DebugForceAsyncWriteDelay > 0) {
                    // Executed synchronously - callback will not be called 
                    TaskCompletionSource<object> completion = new TaskCompletionSource<object>();
                    uint error = sniError;
                    new Timer(obj=>{
                            try {
                                if (_parser.MARSOn) { // Only take reset lock on MARS.
                                    CheckSetResetConnectionState(error, CallbackType.Write);
                                }

                                if (error != TdsEnums.SNI_SUCCESS) {
                                    Bid.Trace("<sc.TdsParser.WritePacket|Info> write async returned error code %d\n", (int)error);
                                    AddError(_parser.ProcessSNIError(this));
                                    ThrowExceptionAndWarning();
                                }
                                AssertValidState();
                                completion.SetResult(null);
                            }
                            catch (Exception e) {
                                completion.SetException(e);
                            }
                        },null,SqlCommand.DebugForceAsyncWriteDelay,Timeout.Infinite);
                    task = completion.Task;
                }

#endif
            else {

                if (_parser.MARSOn) { // Only take reset lock on MARS.
                    CheckSetResetConnectionState(sniError, CallbackType.Write);
                }

                if (sniError == TdsEnums.SNI_SUCCESS) {
                    _lastSuccessfulIOTimer._value = DateTime.UtcNow.Ticks;

                    if (!sync) {
                        // Since there will be no callback, remove the packet from the pending list
                        Debug.Assert(packetPointer != IntPtr.Zero, "Packet added to list has an invalid pointer, can not remove from pending list");
                        RemovePacketFromPendingList(packetPointer);
                    }
                }
                else {
                    Bid.Trace("<sc.TdsParser.WritePacket|Info> write async returned error code %d\n", (int)sniError);
                    AddError(_parser.ProcessSNIError(this));
                    ThrowExceptionAndWarning(callerHasConnectionLock);
                }
                AssertValidState();
            }
            return task;
        }

#pragma warning restore 420 

        // Sends an attention signal - executing thread will consume attn.
        internal void SendAttention(bool mustTakeWriteLock = false) {
            if (!_attentionSent) {
                // Dumps contents of buffer to OOB write (currently only used for
                // attentions.  There is no body for this message
                // Doesn't touch this._outBytesUsed
                if (_parser.State == TdsParserState.Closed || _parser.State == TdsParserState.Broken) {
                    return;
                }

                SNIPacket attnPacket = new SNIPacket(Handle);
                _sniAsyncAttnPacket = attnPacket;

                SNINativeMethodWrapper.SNIPacketSetData(attnPacket, SQL.AttentionHeader, TdsEnums.HEADER_LEN, null, null);

                RuntimeHelpers.PrepareConstrainedRegions();
                try {
                    // Dev11 #344723: SqlClient stress hang System_Data!Tcp::ReadSync via a call to SqlDataReader::Close
                    // Set _attentionSending to true before sending attention and reset after setting _attentionSent
                    // This prevents a race condition between receiving the attention ACK and setting _attentionSent
                    _attentionSending = true;

#if DEBUG
                    if (!_skipSendAttention)
                    {
#endif
                        // Take lock and send attention
                        bool releaseLock = false;
                        if ((mustTakeWriteLock) && (!_parser.Connection.ThreadHasParserLockForClose)) {
                            releaseLock = true;
                            _parser.Connection._parserLock.Wait(canReleaseFromAnyThread: false);
                            _parser.Connection.ThreadHasParserLockForClose = true;
                        }
                        try {
                            // Check again (just in case the connection was closed while we were waiting)
                            if (_parser.State == TdsParserState.Closed || _parser.State == TdsParserState.Broken) {
                                return;
                            }

                            UInt32 sniError;
                            _parser._asyncWrite = false; // stop async write 
                            SNIWritePacket(Handle, attnPacket, out sniError, canAccumulate:false, callerHasConnectionLock: false);
                            Bid.Trace("<sc.TdsParser.SendAttention|Info> Send Attention ASync .\n");
                        }
                        finally {
                            if (releaseLock) {
                                _parser.Connection.ThreadHasParserLockForClose = false;
                                _parser.Connection._parserLock.Release();
                            }
                        }

#if DEBUG
                    }
#endif

                    SetTimeoutSeconds(AttentionTimeoutSeconds); // Initialize new attention timeout of 5 seconds.
                    _attentionSent = true;
                }
                finally {
                    _attentionSending = false;
                }

                if (Bid.AdvancedOn)  {
                    Bid.TraceBin("<sc.TdsParser.WritePacket|INFO|ADV>  Packet sent", _outBuff, (UInt16)_outBytesUsed);
                }
                Bid.Trace("<sc.TdsParser.SendAttention|Info> Attention sent to the server.\n");

                AssertValidState();
            }
        }
       
        private Task WriteSni(bool canAccumulate) {
            // Prepare packet, and write to packet.
            SNIPacket packet = GetResetWritePacket();
            SNINativeMethodWrapper.SNIPacketSetData(packet, _outBuff, _outBytesUsed, _securePasswords, _securePasswordOffsetsInBuffer);
            
            uint sniError;
            Debug.Assert(Parser.Connection._parserLock.ThreadMayHaveLock(), "Thread is writing without taking the connection lock");
            Task task = SNIWritePacket(Handle, packet, out sniError, canAccumulate, callerHasConnectionLock: true);

            // Check to see if the timeout has occured.  This time out code is special case code to allow BCP writes to timeout to fix bug 350558, eventually we should make all writes timeout.
            if (_bulkCopyOpperationInProgress && 0 == GetTimeoutRemaining()) {
                _parser.Connection.ThreadHasParserLockForClose = true;
                try {
                    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));
                    _bulkCopyWriteTimeout = true;
                    SendAttention();
                    _parser.ProcessPendingAck(this);
                    ThrowExceptionAndWarning();
                }
                finally {
                    _parser.Connection.ThreadHasParserLockForClose = false;
                }
            }

            // Special case logic for encryption removal.
            // 
            if (_parser.State == TdsParserState.OpenNotLoggedIn &&
                _parser.EncryptionOptions == EncryptionOptions.LOGIN) {
                // If no error occurred, and we are Open but not logged in, and
                // our encryptionOption state is login, remove the SSL Provider.
                // We only need encrypt the very first packet of the login message to the server.

                // SQL BU DT 332481 - we wanted to encrypt entire login channel, but there is
                // currently no mechanism to communicate this.  Removing encryption post 1st packet
                // is a hard-coded agreement between client and server.  We need some mechanism or
                // common change to be able to make this change in a non-breaking fasion.
                _parser.RemoveEncryption();                        // Remove the SSL Provider.
                _parser.EncryptionOptions = EncryptionOptions.OFF; // Turn encryption off.

                // Since this packet was associated with encryption, dispose and re-create.
                ClearAllWritePackets();
            }

            SniWriteStatisticsAndTracing();

            ResetBuffer();

            AssertValidState();
            return task;
        }

        internal SNIPacket GetResetWritePacket() {
            if (_sniPacket != null) {
                SNINativeMethodWrapper.SNIPacketReset(Handle, SNINativeMethodWrapper.IOType.WRITE, _sniPacket, SNINativeMethodWrapper.ConsumerNumber.SNI_Consumer_SNI);
            }
            else {
                lock (_writePacketLockObject) {
                    _sniPacket = _writePacketCache.Take(Handle);
                }
            }
            return _sniPacket;
        }

        internal void ClearAllWritePackets() {
            if (_sniPacket != null) {
                _sniPacket.Dispose();
                _sniPacket = null;
            }
            lock (_writePacketLockObject) {
                Debug.Assert(_pendingWritePackets.Count == 0 && _asyncWriteCount == 0, "Should not clear all write packets if there are packets pending");
                _writePacketCache.Clear();
            }
        }

        private IntPtr AddPacketToPendingList(SNIPacket packet) {
            Debug.Assert(packet == _sniPacket, "Adding a packet other than the current packet to the pending list");
            _sniPacket = null;
            IntPtr pointer = packet.DangerousGetHandle();
            
            lock (_writePacketLockObject) {
                _pendingWritePackets.Add(pointer, packet);
            }

            return pointer;
        }

        private void RemovePacketFromPendingList(IntPtr pointer) {
            SNIPacket recoveredPacket;

            lock (_writePacketLockObject) {
                if (_pendingWritePackets.TryGetValue(pointer, out recoveredPacket)) {
                    _pendingWritePackets.Remove(pointer);
                    _writePacketCache.Add(recoveredPacket);
                }
#if DEBUG
                else {
                    Debug.Assert(false, "Removing a packet from the pending list that was never added to it");
                }
#endif
            }
        }

        //////////////////////////////////////////////
        // Statistics, Tracing, and related methods //
        //////////////////////////////////////////////

        private void SniReadStatisticsAndTracing() {
            SqlStatistics statistics = Parser.Statistics;
            if (null != statistics) {
                if (statistics.WaitForReply) {
                    statistics.SafeIncrement(ref statistics._serverRoundtrips);
                    statistics.ReleaseAndUpdateNetworkServerTimer();
                }

                statistics.SafeAdd(ref statistics._bytesReceived, _inBytesRead);
                statistics.SafeIncrement(ref statistics._buffersReceived);
            }
        }

        private void SniWriteStatisticsAndTracing() {
            SqlStatistics statistics = _parser.Statistics;
            if (null != statistics) {
                statistics.SafeIncrement(ref statistics._buffersSent);
                statistics.SafeAdd(ref statistics._bytesSent, _outBytesUsed);
                statistics.RequestNetworkServerTimer();
            }

            if (Bid.AdvancedOn) {
                // If we have tracePassword variables set, we are flushing TDSLogin and so we need to
                // blank out password in buffer.  Buffer has already been sent to netlib, so no danger
                // of losing info.
                if (_tracePasswordOffset != 0) {
                    for (int i = _tracePasswordOffset; i < _tracePasswordOffset +
                        _tracePasswordLength; i++) {
                        _outBuff[i] = 0;
                    }

                    // Reset state.
                    _tracePasswordOffset = 0;
                    _tracePasswordLength = 0;
                }
                if (_traceChangePasswordOffset != 0) {
                    for (int i = _traceChangePasswordOffset; i < _traceChangePasswordOffset +
                        _traceChangePasswordLength; i++) {
                        _outBuff[i] = 0;
                    }

                    // Reset state.
                    _traceChangePasswordOffset = 0;
                    _traceChangePasswordLength = 0;
                }
                Bid.TraceBin("<sc.TdsParser.WritePacket|INFO|ADV>  Packet sent", _outBuff, (UInt16)_outBytesUsed);
            }
        }

        [Conditional("DEBUG")]
        void AssertValidState() {
            string assertMessage = null;

            if (_inBytesUsed < 0 || _inBytesRead < 0) {
                assertMessage = string.Format(
                    CultureInfo.InvariantCulture,
                    "either _inBytesUsed or _inBytesRead is negative: {0}, {1}",
                    _inBytesUsed, _inBytesRead);
            }
            else if (_inBytesUsed > _inBytesRead) {
                assertMessage = string.Format(
                    CultureInfo.InvariantCulture,
                    "_inBytesUsed > _inBytesRead: {0} > {1}",
                    _inBytesUsed, _inBytesRead);
            }

            // 

            if (assertMessage != null) {
                Debug.Assert(false, "Invalid TDS Parser State: " + assertMessage);
            }

            Debug.Assert(_inBytesPacket >= 0, "Packet must not be negative");
        }

        
        //////////////////////////////////////////////
        // Errors and Warnings                      //
        //////////////////////////////////////////////

        /// <summary>
        /// True if there is at least one error or warning (not counting the pre-attention errors\warnings)
        /// </summary>
        internal bool HasErrorOrWarning {
            get {
                return _hasErrorOrWarning;
            }
        }

        /// <summary>
        /// Adds an error to the error collection
        /// </summary>
        /// <param name="error"></param>
        internal void AddError(SqlError error) {
            Debug.Assert(error != null, "Trying to add a null error");

            // Switch to sync once we see an error
            _syncOverAsync = true;

            lock (_errorAndWarningsLock) {
                _hasErrorOrWarning = true;
                if (_errors == null) {
                    _errors = new SqlErrorCollection();
                }
                _errors.Add(error);
            }
        }

        /// <summary>
        /// Gets the number of errors currently in the error collection
        /// </summary>
        internal int ErrorCount {
            get {
                int count = 0;
                lock (_errorAndWarningsLock){
                    if (_errors != null) {
                        count = _errors.Count;
                    }
                }
                return count;
            }
        }

        /// <summary>
        /// Adds an warning to the warning collection
        /// </summary>
        /// <param name="error"></param>
        internal void AddWarning(SqlError error) {
            Debug.Assert(error != null, "Trying to add a null error");

            // Switch to sync once we see a warning
            _syncOverAsync = true;

            lock (_errorAndWarningsLock){
                _hasErrorOrWarning = true;
                if (_warnings == null) {
                    _warnings = new SqlErrorCollection();
                }
                _warnings.Add(error);
            }
        }
        
        /// <summary>
        /// Gets the number of warnings currently in the warning collection
        /// </summary>
        internal int WarningCount {
            get {
                int count = 0;
                lock (_errorAndWarningsLock){
                    if (_warnings != null) {
                        count = _warnings.Count;
                    }
                }
                return count;
            }
        }

        /// <summary>
        /// Gets the number of errors currently in the pre-attention error collection
        /// </summary>
        internal int PreAttentionErrorCount {
            get {
                int count = 0;
                lock (_errorAndWarningsLock){
                    if (_preAttentionErrors != null) {
                        count = _preAttentionErrors.Count;
                    }
                }
                return count;
            }
        }

        /// <summary>
        /// Gets the number of errors currently in the pre-attention warning collection
        /// </summary>
        internal int PreAttentionWarningCount {
            get {
                int count = 0;
                lock (_errorAndWarningsLock){
                    if (_preAttentionWarnings != null) {
                        count = _preAttentionWarnings.Count;
                    }
                }
                return count;
            }
        }

        /// <summary>
        /// Gets the full list of errors and warnings (including the pre-attention ones), then wipes all error and warning lists
        /// </summary>
        /// <param name="broken">If true, the connection should be broken</param>
        /// <returns>An array containing all of the errors and warnings</returns>
        internal SqlErrorCollection GetFullErrorAndWarningCollection(out bool broken) {
            SqlErrorCollection allErrors = new SqlErrorCollection();
            broken = false;
            
            lock (_errorAndWarningsLock){
                _hasErrorOrWarning = false;

                // Merge all error lists, then reset them
                AddErrorsToCollection(_errors, ref allErrors, ref broken);
                AddErrorsToCollection(_warnings, ref allErrors, ref broken);
                _errors = null;
                _warnings = null;

                // We also process the pre-attention error lists here since, if we are here and they are populated, then an error occured while sending attention so we should show the errors now (otherwise they'd be lost)
                AddErrorsToCollection(_preAttentionErrors, ref allErrors, ref broken);
                AddErrorsToCollection(_preAttentionWarnings, ref allErrors, ref broken);
                _preAttentionErrors = null;
                _preAttentionWarnings = null;
            }

            return allErrors;
        }

        private void AddErrorsToCollection(SqlErrorCollection inCollection, ref SqlErrorCollection collectionToAddTo, ref bool broken) {
            if (inCollection != null) {
                foreach (SqlError error in inCollection) {
                    collectionToAddTo.Add(error);
                    broken |= (error.Class >= TdsEnums.FATAL_ERROR_CLASS);
                }
            }
        }

        /// <summary>
        /// Stores away current errors and warnings so that an attention can be processed
        /// </summary>
        internal void StoreErrorAndWarningForAttention() {
            lock (_errorAndWarningsLock){
                Debug.Assert(_preAttentionErrors == null && _preAttentionWarnings == null, "Can't store errors for attention because there are already errors stored");

                _hasErrorOrWarning = false;

                _preAttentionErrors = _errors;
                _preAttentionWarnings = _warnings;

                _errors = null;
                _warnings = null;
            }
        }

        /// <summary>
        /// Restores errors and warnings that were stored in order to process an attention
        /// </summary>
        internal void RestoreErrorAndWarningAfterAttention() {
            lock (_errorAndWarningsLock){
                Debug.Assert(_errors == null && _warnings == null, "Can't restore errors after attention because there are already other errors");

                _hasErrorOrWarning = (((_preAttentionErrors != null) && (_preAttentionErrors.Count > 0)) || ((_preAttentionWarnings != null) && (_preAttentionWarnings.Count > 0)));

                _errors = _preAttentionErrors;
                _warnings = _preAttentionWarnings;

                _preAttentionErrors = null;
                _preAttentionWarnings = null;
            }
        }

        /// <summary>
        /// Checks if an error is stored in _error and, if so, throws an error
        /// </summary>
        internal void CheckThrowSNIException() {
            if (HasErrorOrWarning) {
                ThrowExceptionAndWarning();
            }
        }

        /// <summary>
        /// Debug Only: Ensures that the TdsParserStateObject has no lingering state and can safely be re-used
        /// </summary>
        [Conditional("DEBUG")]
        internal void AssertStateIsClean() {
            // If our TdsParser is closed or broken, then we don't really care about our state
            var parser = _parser;
            if ((parser != null) && (parser.State != TdsParserState.Closed) && (parser.State != TdsParserState.Broken)) {
                // Async reads
                Debug.Assert(_snapshot == null && !_snapshotReplay, "StateObj has leftover snapshot state");
                Debug.Assert(!_asyncReadWithoutSnapshot, "StateObj has AsyncReadWithoutSnapshot still enabled");
                Debug.Assert(_executionContext == null, "StateObj has a stored execution context from an async read");
                // Async writes
                Debug.Assert(_asyncWriteCount == 0, "StateObj still has outstanding async writes");
                Debug.Assert(_delayedWriteAsyncCallbackException == null, "StateObj has an unobserved exceptions from an async write");
                // Attention\Cancellation\Timeouts
                Debug.Assert(!_attentionReceived && !_attentionSent && !_attentionSending, string.Format("StateObj is still dealing with attention: Sent: {0}, Received: {1}, Sending: {2}", _attentionSent, _attentionReceived, _attentionSending));
                Debug.Assert(!_cancelled, "StateObj still has cancellation set");
                Debug.Assert(!_internalTimeout, "StateObj still has internal timeout set");
                // Errors and Warnings
                Debug.Assert(!_hasErrorOrWarning, "StateObj still has stored errors or warnings");
            }
        }
        
#if DEBUG
        internal void CompletePendingReadWithSuccess(bool resetForcePendingReadsToWait) {
            var realNetworkPacketTaskSource = _realNetworkPacketTaskSource;
            var networkPacketTaskSource = _networkPacketTaskSource;

            Debug.Assert(_forcePendingReadsToWaitForUser, "Not forcing pends to wait for user - can't force complete");
            Debug.Assert(networkPacketTaskSource != null, "No pending read to complete");
            
            try {
                if (realNetworkPacketTaskSource != null) {
                    // Wait for the real read to complete
                    realNetworkPacketTaskSource.Task.Wait();
                }
            }
            finally {
                if (networkPacketTaskSource != null) {
                    if (resetForcePendingReadsToWait) {
                        _forcePendingReadsToWaitForUser = false;
                    }

                    networkPacketTaskSource.TrySetResult(null);
                }
            }
        }

        internal void CompletePendingReadWithFailure(int errorCode, bool resetForcePendingReadsToWait) {
            var realNetworkPacketTaskSource = _realNetworkPacketTaskSource;
            var networkPacketTaskSource = _networkPacketTaskSource;

            Debug.Assert(_forcePendingReadsToWaitForUser, "Not forcing pends to wait for user - can't force complete");
            Debug.Assert(networkPacketTaskSource != null, "No pending read to complete");
            
            try {
                if (realNetworkPacketTaskSource != null) {
                    // Wait for the real read to complete
                    realNetworkPacketTaskSource.Task.Wait();
                }
            }
            finally {
                if (networkPacketTaskSource != null) {
                    if (resetForcePendingReadsToWait) {
                        _forcePendingReadsToWaitForUser = false;
                    }

                    AddError(new SqlError(errorCode, 0x00, TdsEnums.FATAL_ERROR_CLASS, _parser.Server, string.Empty, string.Empty, 0));
                    try {
                        ThrowExceptionAndWarning();
                    }
                    catch (Exception ex) {
                        networkPacketTaskSource.TrySetException(ex);
                    }
                }
            }
        }
#endif

        internal void CloneCleanupAltMetaDataSetArray()
        {
            if (_snapshot != null) {
                _snapshot.CloneCleanupAltMetaDataSetArray();
            }
        }

        class PacketData {
            public byte[] Buffer;
            public int Read;
#if DEBUG
            public StackTrace Stack;
#endif
        }
        
        class StateSnapshot
        {
            private List<PacketData> _snapshotInBuffs;
            private int _snapshotInBuffCurrent = 0;
            private int _snapshotInBytesUsed = 0;
            private int _snapshotInBytesPacket = 0;
            private bool _snapshotPendingData = false;
            private bool _snapshotErrorTokenReceived = false;
            private bool _snapshotHasOpenResult = false;
            private bool _snapshotReceivedColumnMetadata = false;
            private bool _snapshotAttentionReceived;
            private byte _snapshotMessageStatus;

            private NullBitmap _snapshotNullBitmapInfo;
            private ulong _snapshotLongLen;
            private ulong _snapshotLongLenLeft;
            private _SqlMetaDataSet _snapshotCleanupMetaData;
            private _SqlMetaDataSetCollection _snapshotCleanupAltMetaDataSetArray;

            private readonly TdsParserStateObject _stateObj;

            public StateSnapshot(TdsParserStateObject state) {
                _snapshotInBuffs = new List<PacketData>();
                _stateObj = state;
            }

#if DEBUG
            private int _rollingPend = 0;
            private int _rollingPendCount = 0;

            internal bool DoPend() {
                if (_failAsyncPends || !_forceAllPends) {
                    return false;
                }

                if (_rollingPendCount == _rollingPend) {
                    _rollingPend++;
                    _rollingPendCount = 0;
                    return true;
                }

                _rollingPendCount++;
                return false;
            }
#endif

            internal void CloneNullBitmapInfo() {
                if (_stateObj._nullBitmapInfo.ReferenceEquals(_snapshotNullBitmapInfo)) {
                    _stateObj._nullBitmapInfo = _stateObj._nullBitmapInfo.Clone();
                }
            }

            internal void CloneCleanupAltMetaDataSetArray() {
                if (_stateObj._cleanupAltMetaDataSetArray != null && object.ReferenceEquals(_snapshotCleanupAltMetaDataSetArray, _stateObj._cleanupAltMetaDataSetArray)) {
                    _stateObj._cleanupAltMetaDataSetArray = (_SqlMetaDataSetCollection)_stateObj._cleanupAltMetaDataSetArray.Clone();
                }
            }

            internal void PushBuffer(byte[] buffer, int read) {
                Debug.Assert(!_snapshotInBuffs.Any(b => object.ReferenceEquals(b, buffer)));

                PacketData packetData = new PacketData();
                packetData.Buffer = buffer;
                packetData.Read = read;
#if DEBUG
                packetData.Stack = _stateObj._lastStack;
#endif

                _snapshotInBuffs.Add(packetData);
            }

#if DEBUG
            internal void AssertCurrent()
            {
                Debug.Assert(_snapshotInBuffCurrent == _snapshotInBuffs.Count, "Should not be reading new packets when not replaying last packet");
            }
            internal void CheckStack(StackTrace trace)
            {
                PacketData prev = _snapshotInBuffs[_snapshotInBuffCurrent - 1];
                if (prev.Stack == null)
                {
                    prev.Stack = trace;
                }
                else
                {
                    Debug.Assert(_stateObj._permitReplayStackTraceToDiffer || prev.Stack.ToString() == trace.ToString(), "The stack trace on subsequent replays should be the same");
                }
            }
#endif 

            internal bool Replay() {
                if (_snapshotInBuffCurrent < _snapshotInBuffs.Count) {
                    PacketData next = _snapshotInBuffs[_snapshotInBuffCurrent];
                    _stateObj._inBuff = next.Buffer;
                    _stateObj._inBytesUsed = 0;
                    _stateObj._inBytesRead = next.Read;
                    _snapshotInBuffCurrent++;
                    return true;
                }

                return false;
            }

            internal void Snap()
            {
                _snapshotInBuffs.Clear();
                _snapshotInBuffCurrent = 0;
                _snapshotInBytesUsed = _stateObj._inBytesUsed;
                _snapshotInBytesPacket = _stateObj._inBytesPacket;
                _snapshotPendingData = _stateObj._pendingData;
                _snapshotErrorTokenReceived = _stateObj._errorTokenReceived;
                _snapshotMessageStatus = _stateObj._messageStatus;
                // _nullBitmapInfo must be cloned before it is updated
                _snapshotNullBitmapInfo = _stateObj._nullBitmapInfo;
                _snapshotLongLen = _stateObj._longlen;
                _snapshotLongLenLeft = _stateObj._longlenleft;
                _snapshotCleanupMetaData = _stateObj._cleanupMetaData;
                // _cleanupAltMetaDataSetArray must be cloned bofore it is updated
                _snapshotCleanupAltMetaDataSetArray = _stateObj._cleanupAltMetaDataSetArray;
                _snapshotHasOpenResult = _stateObj._hasOpenResult;
                _snapshotReceivedColumnMetadata = _stateObj._receivedColMetaData;
                _snapshotAttentionReceived = _stateObj._attentionReceived;
#if DEBUG
                _rollingPend = 0;
                _rollingPendCount = 0;
                _stateObj._lastStack = null;
                Debug.Assert(_stateObj._bTmpRead == 0, "Has partially read data when snapshot taken");
                Debug.Assert(_stateObj._partialHeaderBytesRead == 0, "Has partially read header when shapshot taken");
#endif

                PushBuffer(_stateObj._inBuff, _stateObj._inBytesRead);
            }

            internal void ResetSnapshotState() {
                // go back to the beginning
                _snapshotInBuffCurrent = 0;

                Replay();

                _stateObj._inBytesUsed = _snapshotInBytesUsed;
                _stateObj._inBytesPacket = _snapshotInBytesPacket;
                _stateObj._pendingData = _snapshotPendingData;
                _stateObj._errorTokenReceived = _snapshotErrorTokenReceived;
                _stateObj._messageStatus = _snapshotMessageStatus;
                _stateObj._nullBitmapInfo = _snapshotNullBitmapInfo;
                _stateObj._cleanupMetaData = _snapshotCleanupMetaData;
                _stateObj._cleanupAltMetaDataSetArray = _snapshotCleanupAltMetaDataSetArray;
                _stateObj._hasOpenResult = _snapshotHasOpenResult;
                _stateObj._receivedColMetaData = _snapshotReceivedColumnMetadata;
                _stateObj._attentionReceived = _snapshotAttentionReceived;

                // Reset partially read state (these only need to be maintained if doing async without snapshot)
                _stateObj._bTmpRead = 0;
                _stateObj._partialHeaderBytesRead = 0;

                // reset plp state
                _stateObj._longlen = _snapshotLongLen;
                _stateObj._longlenleft = _snapshotLongLenLeft;

                _stateObj._snapshotReplay = true;

                _stateObj.AssertValidState();
            }

            internal void PrepareReplay() {
                ResetSnapshotState();
            }
        }
        
/*

// leave this in. comes handy if you have to do Console.WriteLine style debugging ;)
        private void DumpBuffer() {
            Console.WriteLine("dumping buffer");
            Console.WriteLine("_inBytesRead = {0}", _inBytesRead);
            Console.WriteLine("_inBytesUsed = {0}", _inBytesUsed);
            int cc = 0; // character counter
            int i;
            Console.WriteLine("used buffer:");
            for (i=0; i< _inBytesUsed; i++) {
                if (cc==16) {
                    Console.WriteLine();
                    cc = 0;
                }
                Console.Write("{0,-2:X2} ", _inBuff[i]);
                cc++;
            }
            if (cc>0) {
                Console.WriteLine();
            }

            cc = 0;
            Console.WriteLine("unused buffer:");
            for (i=_inBytesUsed; i<_inBytesRead; i++) {
                if (cc==16) {
                    Console.WriteLine();
                    cc = 0;
                }
                Console.Write("{0,-2:X2} ", _inBuff[i]);
                cc++;
            }
            if (cc>0) {
                Console.WriteLine();
            }
        }
*/
    }