internal Task ValidateAndReconnect(Action beforeDisconnect, int timeout) { Task runningReconnect = _currentReconnectionTask; // This loop in the end will return not completed reconnect task or null while (runningReconnect != null && runningReconnect.IsCompleted) { // clean current reconnect task (if it is the same one we checked Interlocked.CompareExchange <Task>(ref _currentReconnectionTask, null, runningReconnect); // make sure nobody started new task (if which case we did not clean it) runningReconnect = _currentReconnectionTask; } if (runningReconnect == null) { if (_connectRetryCount > 0) { SqlInternalConnectionTds tdsConn = GetOpenTdsConnection(); if (tdsConn._sessionRecoveryAcknowledged) { TdsParserStateObject stateObj = tdsConn.Parser._physicalStateObj; if (!stateObj.ValidateSNIConnection()) { if (tdsConn.Parser._sessionPool != null) { if (tdsConn.Parser._sessionPool.ActiveSessionsCount > 0) { // >1 MARS session if (beforeDisconnect != null) { beforeDisconnect(); } OnError(SQL.CR_UnrecoverableClient(ClientConnectionId), true, null); } } SessionData cData = tdsConn.CurrentSessionData; cData.AssertUnrecoverableStateCountIsCorrect(); if (cData._unrecoverableStatesCount == 0) { bool callDisconnect = false; lock (_reconnectLock) { runningReconnect = _currentReconnectionTask; // double check after obtaining the lock if (runningReconnect == null) { if (cData._unrecoverableStatesCount == 0) { // could change since the first check, but now is stable since connection is know to be broken _originalConnectionId = ClientConnectionId; _recoverySessionData = cData; if (beforeDisconnect != null) { beforeDisconnect(); } try { _supressStateChangeForReconnection = true; tdsConn.DoomThisConnection(); } catch (SqlException) { } runningReconnect = Task.Run(() => ReconnectAsync(timeout)); // if current reconnect is not null, somebody already started reconnection task - some kind of race condition Debug.Assert(_currentReconnectionTask == null, "Duplicate reconnection tasks detected"); _currentReconnectionTask = runningReconnect; } } else { callDisconnect = true; } } if (callDisconnect && beforeDisconnect != null) { beforeDisconnect(); } } else { if (beforeDisconnect != null) { beforeDisconnect(); } OnError(SQL.CR_UnrecoverableServer(ClientConnectionId), true, null); } } // ValidateSNIConnection } // sessionRecoverySupported } // connectRetryCount>0 } else { // runningReconnect = null if (beforeDisconnect != null) { beforeDisconnect(); } } return(runningReconnect); }
internal Task ValidateAndReconnect(Action beforeDisconnect, int timeout) { Task runningReconnect = _currentReconnectionTask; // This loop in the end will return not completed reconnect task or null while (runningReconnect != null && runningReconnect.IsCompleted) { // clean current reconnect task (if it is the same one we checked Interlocked.CompareExchange <Task>(ref _currentReconnectionTask, null, runningReconnect); // make sure nobody started new task (if which case we did not clean it) runningReconnect = _currentReconnectionTask; } if (runningReconnect == null) { if (_connectRetryCount > 0) { SqlInternalConnectionTds tdsConn = GetOpenTdsConnection(); if (tdsConn._sessionRecoveryAcknowledged) { TdsParser tdsParser = tdsConn.Parser; if (!tdsParser._physicalStateObj.ValidateSNIConnection()) { if ((tdsParser._sessionPool != null) && (tdsParser._sessionPool.ActiveSessionsCount > 0)) { // >1 MARS session beforeDisconnect?.Invoke(); OnError(SQL.CR_UnrecoverableClient(ClientConnectionId), true, null); } SessionData cData = tdsConn.CurrentSessionData; cData.AssertUnrecoverableStateCountIsCorrect(); if (cData._unrecoverableStatesCount == 0) { TaskCompletionSource <object> reconnectCompletionSource = new TaskCompletionSource <object>(); runningReconnect = Interlocked.CompareExchange(ref _currentReconnectionTask, reconnectCompletionSource.Task, null); if (runningReconnect == null) { if (cData._unrecoverableStatesCount == 0) { // could change since the first check, but now is stable since connection is know to be broken _originalConnectionId = ClientConnectionId; _recoverySessionData = cData; beforeDisconnect?.Invoke(); try { _supressStateChangeForReconnection = true; tdsConn.DoomThisConnection(); } catch (SqlException) { } Task.Run(() => ReconnectAsync(timeout).ContinueWith(t => { if (t.IsFaulted) { reconnectCompletionSource.SetException(t.Exception); } else if (t.IsCanceled) { reconnectCompletionSource.SetCanceled(); } else { reconnectCompletionSource.SetResult(null); } })); runningReconnect = reconnectCompletionSource.Task; } } else { beforeDisconnect?.Invoke(); } } else { beforeDisconnect?.Invoke(); OnError(SQL.CR_UnrecoverableServer(ClientConnectionId), true, null); } } // ValidateSNIConnection } // sessionRecoverySupported } // connectRetryCount>0 } else { // runningReconnect = null beforeDisconnect?.Invoke(); } return(runningReconnect); }