/// <summary>
        /// Static Method.
        /// Given two contexts, choose one to update in the cache. Chooses based on expiration time.
        /// </summary>
        /// <param name="context1">Context1</param>
        /// <param name="context2">Context2</param>
        internal static DbConnectionPoolAuthenticationContext ChooseAuthenticationContextToUpdate(DbConnectionPoolAuthenticationContext context1, DbConnectionPoolAuthenticationContext context2) {

            Debug.Assert(context1 != null, "context1 should not be null.");
            Debug.Assert(context2 != null, "context2 should not be null.");

            return context1.ExpirationTime > context2.ExpirationTime ? context1 : context2;
        }
        /// <summary>
        /// Get the Federated Authentication Token.
        /// </summary>
        /// <param name="fedAuthInfo">Information obtained from server as Federated Authentication Info.</param>
        /// <returns>SqlFedAuthToken</returns>
        internal SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) {

            Debug.Assert(fedAuthInfo != null, "fedAuthInfo should not be null.");

            // No:of milliseconds to sleep for the inital back off.
            int sleepInterval = 100;

            // No:of attempts, for tracing purposes, if we underwent retries.
            int numberOfAttempts = 0;

            // Object that will be returned to the caller, containing all required data about the token.
            SqlFedAuthToken fedAuthToken = new SqlFedAuthToken();

            // Username to use in error messages.
            string username = null;

            while (true) {
                numberOfAttempts++;
                try {
                    if (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
                        username = TdsEnums.NTAUTHORITYANONYMOUSLOGON;
                        fedAuthToken.accessToken = ADALNativeWrapper.ADALGetAccessTokenForWindowsIntegrated(fedAuthInfo.stsurl,
                                                                                                                fedAuthInfo.spn,
                                                                                                                _clientConnectionId, ActiveDirectoryAuthentication.AdoClientId,
                                                                                                                ref fedAuthToken.expirationFileTime);
                    }
                    else if (_credential != null) {
                        username = _credential.UserId;
                        fedAuthToken.accessToken = ADALNativeWrapper.ADALGetAccessToken(_credential.UserId,
                                                                                            _credential.Password,
                                                                                            fedAuthInfo.stsurl,
                                                                                            fedAuthInfo.spn,
                                                                                            _clientConnectionId,
                                                                                            ActiveDirectoryAuthentication.AdoClientId,
                                                                                            ref fedAuthToken.expirationFileTime);
                    }
                    else {
                        username = ConnectionOptions.UserID;
                        fedAuthToken.accessToken = ADALNativeWrapper.ADALGetAccessToken(ConnectionOptions.UserID,
                                                                                            ConnectionOptions.Password,
                                                                                            fedAuthInfo.stsurl,
                                                                                            fedAuthInfo.spn,
                                                                                            _clientConnectionId,
                                                                                            ActiveDirectoryAuthentication.AdoClientId,
                                                                                            ref fedAuthToken.expirationFileTime);
                    }

                    Debug.Assert(fedAuthToken.accessToken != null, "AccessToken should not be null.");
#if DEBUG
                    if (_forceAdalRetry) {
                        // 3399614468 is 0xCAA20004L just for testing.
                        throw new AdalException("Force retry in GetFedAuthToken", ActiveDirectoryAuthentication.GetAccessTokenTansisentError, 3399614468, 6);
                    }
#endif
                    // Break out of the retry loop in successful case.
                    break;
                }
                catch (AdalException adalException) {

                    uint errorCategory = adalException.GetCategory();

                    if (ActiveDirectoryAuthentication.GetAccessTokenTansisentError != errorCategory
                        || _timeout.IsExpired
                        || _timeout.MillisecondsRemaining <= sleepInterval) {

                        string errorStatus = adalException.GetStatus().ToString("X");

                        Bid.Trace("<sc.SqlInternalConnectionTds.GetFedAuthToken.ADALException category:> %d#  <error:> %s#\n", (int)errorCategory, errorStatus);

                        // Error[0]
                        SqlErrorCollection sqlErs = new SqlErrorCollection();
                        sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, Res.GetString(Res.SQL_ADALFailure, username, ConnectionOptions.Authentication.ToString("G")), ActiveDirectoryAuthentication.AdalGetAccessTokenFunctionName, 0));

                        // Error[1]
                        string errorMessage1 = Res.GetString(Res.SQL_ADALInnerException, errorStatus, adalException.GetState());
                        sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, errorMessage1, ActiveDirectoryAuthentication.AdalGetAccessTokenFunctionName, 0));

                        // Error[2]
                        if (!string.IsNullOrEmpty(adalException.Message)) {
                            sqlErs.Add(new SqlError(0, (byte)0x00, (byte)TdsEnums.MIN_ERROR_CLASS, ConnectionOptions.DataSource, adalException.Message, ActiveDirectoryAuthentication.AdalGetAccessTokenFunctionName, 0));
                        }
                        SqlException exc = SqlException.CreateException(sqlErs, "", this);
                        throw exc;
                    }

                    Bid.Trace("<sc.SqlInternalConnectionTds.GetFedAuthToken|ADV> %d#, sleeping %d{Milliseconds}\n", ObjectID, sleepInterval);
                    Bid.Trace("<sc.SqlInternalConnectionTds.GetFedAuthToken|ADV> %d#, remaining %d{Milliseconds}\n", ObjectID, _timeout.MillisecondsRemaining);

                    Thread.Sleep(sleepInterval);
                    sleepInterval *= 2;
                }
            }

            Debug.Assert(fedAuthToken != null, "fedAuthToken should not be null.");
            Debug.Assert(fedAuthToken.accessToken != null && fedAuthToken.accessToken.Length > 0, "fedAuthToken.accessToken should not be null or empty.");

            // Store the newly generated token in _newDbConnectionPoolAuthenticationContext, only if using pooling.
            if (_dbConnectionPool != null) {
                DateTime expirationTime = DateTime.FromFileTimeUtc(fedAuthToken.expirationFileTime);
                _newDbConnectionPoolAuthenticationContext = new DbConnectionPoolAuthenticationContext(fedAuthToken.accessToken, expirationTime);
            }

            Bid.Trace("<sc.SqlInternalConnectionTds.GetFedAuthToken> %d#, Finished generating federated authentication token.\n", ObjectID);

            return fedAuthToken;
        }
        /// <summary>
        /// Tries to acquire a lock on the authentication context. If successful in acquiring the lock, gets a new token and assigns it in the out parameter. Else returns false.
        /// </summary>
        /// <param name="fedAuthInfo">Federated Authentication Info</param>
        /// <param name="dbConnectionPoolAuthenticationContext">Authentication Context cached in the connection pool.</param>
        /// <param name="fedAuthToken">Out parameter, carrying the token if we acquired a lock and got the token.</param>
        /// <returns></returns>
        internal bool TryGetFedAuthTokenLocked(SqlFedAuthInfo fedAuthInfo, DbConnectionPoolAuthenticationContext dbConnectionPoolAuthenticationContext, out SqlFedAuthToken fedAuthToken) {

            Debug.Assert(fedAuthInfo != null, "fedAuthInfo should not be null.");
            Debug.Assert(dbConnectionPoolAuthenticationContext != null, "dbConnectionPoolAuthenticationContext should not be null.");

            fedAuthToken = null;

            // Variable which indicates if we did indeed manage to acquire the lock on the authentication context, to try update it.
            bool authenticationContextLocked = false;

            // Prepare CER to ensure the lock on authentication context is released.
            RuntimeHelpers.PrepareConstrainedRegions();
            try {
                // Try to obtain a lock on the context. If acquired, this thread got the opportunity to update.
                // Else some other thread is already updating it, so just proceed forward with the existing token in the cache.
                if (dbConnectionPoolAuthenticationContext.LockToUpdate()) {
                    Bid.Trace("<sc.SqlInternalConnectionTds.TryGetFedAuthTokenLocked> %d#, Acquired the lock to update the authentication context.The expiration time is %s. Current Time is %s.\n", ObjectID, dbConnectionPoolAuthenticationContext.ExpirationTime.ToLongTimeString(), DateTime.UtcNow.ToLongTimeString());

                    authenticationContextLocked = true;
                }
                else {
                    Bid.Trace("<sc.SqlInternalConnectionTds.TryGetFedAuthTokenLocked> %d#, Refreshing the context is already in progress by another thread.\n", ObjectID);
                }

                if (authenticationContextLocked) {
                    // Get the Federated Authentication Token.
                    fedAuthToken = GetFedAuthToken(fedAuthInfo);
                    Debug.Assert(fedAuthToken != null, "fedAuthToken should not be null.");
                }
            }
            finally {
                if (authenticationContextLocked) {
                    // Release the lock we took on the authentication context, even if we have not yet updated the cache with the new context. Login process can fail at several places after this step and so there is no guarantee that the new context will make it to the cache. So we shouldn't miss resetting the flag. With the reset, at-least another thread may have a chance to update it.
                    dbConnectionPoolAuthenticationContext.ReleaseLockToUpdate();
                }
            }

            return authenticationContextLocked;
        }
        /// <summary>
        /// Static Method.
        /// Given two contexts, choose one to update in the cache. Chooses based on expiration time.
        /// </summary>
        /// <param name="context1">Context1</param>
        /// <param name="context2">Context2</param>
        internal static DbConnectionPoolAuthenticationContext ChooseAuthenticationContextToUpdate(DbConnectionPoolAuthenticationContext context1, DbConnectionPoolAuthenticationContext context2)
        {
            Debug.Assert(context1 != null, "context1 should not be null.");
            Debug.Assert(context2 != null, "context2 should not be null.");

            return(context1.ExpirationTime > context2.ExpirationTime ? context1 : context2);
        }