diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index 51109fc388..4fc7e89f67 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -149,7 +149,7 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor DbConnectionPool pool = null; if (null != _poolGroupOptions) { - DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity; + DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.s_noIdentity; if (_poolGroupOptions.PoolByIdentity) { diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index 06e35fbe13..84a9fb727c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -73,6 +73,9 @@ Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContext.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.cs + Microsoft\Data\ProviderBase\DbConnectionPoolAuthenticationContextKey.cs @@ -482,7 +485,9 @@ - + + Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.cs + Microsoft\Data\SqlClient\EnclaveDelegate.cs @@ -490,7 +495,6 @@ Microsoft\Data\SqlClient\EnclavePackage.cs - @@ -651,7 +655,9 @@ - + + Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.Unix.cs + @@ -663,7 +669,9 @@ - + + Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.Windows.cs + Common\Interop\Windows\kernel32\Interop.LoadLibraryEx.cs @@ -808,7 +816,9 @@ - + + Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.Unix.cs + Common\System\Net\Security\NegotiateStreamPal.Unix.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs index 11c153e5b3..4a284d64a9 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.NetCoreApp.cs @@ -26,28 +26,19 @@ private bool IsBlockingPeriodEnabled() { return true; } - var policy = poolGroupConnectionOptions.PoolBlockingPeriod; - switch (policy) + switch (poolGroupConnectionOptions.PoolBlockingPeriod) { case PoolBlockingPeriod.Auto: - { - return !ADP.IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource); - } + return !ADP.IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource); case PoolBlockingPeriod.AlwaysBlock: - { - return true; //Enabled - } + return true; case PoolBlockingPeriod.NeverBlock: - { - return false; //Disabled - } + return false; default: - { - //we should never get into this path. - Debug.Fail("Unknown PoolBlockingPeriod. Please specify explicit results in above switch case statement."); - return true; - } + Debug.Fail("Unknown PoolBlockingPeriod. Please specify explicit results in above switch case statement."); + return true; + } } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index c6f5a39693..7fe76d8c3b 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -7,6 +7,9 @@ using System.Collections.Generic; using System.Data.Common; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using System.Transactions; @@ -15,7 +18,7 @@ namespace Microsoft.Data.ProviderBase { - sealed internal partial class DbConnectionPool + internal sealed partial class DbConnectionPool { private enum State { @@ -27,21 +30,17 @@ private enum State // This class is a way to stash our cloned Tx key for later disposal when it's no longer needed. // We can't get at the key in the dictionary without enumerating entries, so we stash an extra // copy as part of the value. - sealed private class TransactedConnectionList : List + private sealed class TransactedConnectionList : List { - private Transaction _transaction; - internal TransactedConnectionList(int initialAllocation, Transaction tx) : base(initialAllocation) + private readonly Transaction _transaction; + + internal TransactedConnectionList(int initialAllocation, Transaction tx) + : base(initialAllocation) { _transaction = tx; } - internal void Dispose() - { - if (null != _transaction) - { - _transaction.Dispose(); - } - } + internal void Dispose() => _transaction?.Dispose(); } private sealed class PendingGetConnection @@ -53,49 +52,37 @@ public PendingGetConnection(long dueTime, DbConnection owner, TaskCompletionSour Completion = completion; UserOptions = userOptions; } + public long DueTime { get; private set; } public DbConnection Owner { get; private set; } public TaskCompletionSource Completion { get; private set; } public DbConnectionOptions UserOptions { get; private set; } } - sealed private class TransactedConnectionPool + private sealed class TransactedConnectionPool { - Dictionary _transactedCxns; + private static int s_objectTypeCount; - DbConnectionPool _pool; - - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); + private readonly Dictionary _transactedCxns; + private readonly DbConnectionPool _pool; + private readonly int _objectID; internal TransactedConnectionPool(DbConnectionPool pool) { - Debug.Assert(null != pool, "null pool?"); - + Debug.Assert(pool != null, "null pool?"); + _objectID = Interlocked.Increment(ref s_objectTypeCount); _pool = pool; _transactedCxns = new Dictionary(); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Constructed for connection pool {1}", ObjectID, _pool.ObjectID); } - internal int ObjectID - { - get - { - return _objectID; - } - } + internal int ObjectID => _objectID; - internal DbConnectionPool Pool - { - get - { - return _pool; - } - } + internal DbConnectionPool Pool => _pool; internal DbConnectionInternal GetTransactedObject(Transaction transaction) { - Debug.Assert(null != transaction, "null transaction?"); + Debug.Assert(transaction != null, "null transaction?"); DbConnectionInternal transactedObject = null; @@ -131,7 +118,7 @@ internal DbConnectionInternal GetTransactedObject(Transaction transaction) } } - if (null != transactedObject) + if (transactedObject != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Popped.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); } @@ -140,8 +127,8 @@ internal DbConnectionInternal GetTransactedObject(Transaction transaction) internal void PutTransactedObject(Transaction transaction, DbConnectionInternal transactedObject) { - Debug.Assert(null != transaction, "null transaction?"); - Debug.Assert(null != transactedObject, "null transactedObject?"); + Debug.Assert(transaction != null, "null transaction?"); + Debug.Assert(transactedObject != null, "null transactedObject?"); TransactedConnectionList connections; bool txnFound = false; @@ -213,7 +200,7 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal } finally { - if (null != transactionClone) + if (transactionClone != null) { if (newConnections != null) { @@ -237,7 +224,6 @@ internal void PutTransactedObject(Transaction transaction, DbConnectionInternal internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Transaction Completed", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); - TransactedConnectionList connections; int entry = -1; // NOTE: because TransactionEnded is an asynchronous notification, there's no guarantee @@ -250,7 +236,7 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra lock (_transactedCxns) { - if (_transactedCxns.TryGetValue(transaction, out connections)) + if (_transactedCxns.TryGetValue(transaction, out TransactedConnectionList connections)) { Debug.Assert(connections != null); @@ -346,79 +332,72 @@ internal WaitHandle[] GetHandles(bool withCreate) } } - private const int MAX_Q_SIZE = (int)0x00100000; + private const int MAX_Q_SIZE = 0x00100000; // The order of these is important; we want the WaitAny call to be signaled // for a free object before a creation signal. Only the index first signaled // object is returned from the WaitAny call. - private const int SEMAPHORE_HANDLE = (int)0x0; - private const int ERROR_HANDLE = (int)0x1; - private const int CREATION_HANDLE = (int)0x2; - private const int BOGUS_HANDLE = (int)0x3; + private const int SEMAPHORE_HANDLE = 0x0; + private const int ERROR_HANDLE = 0x1; + private const int CREATION_HANDLE = 0x2; + private const int BOGUS_HANDLE = 0x3; private const int ERROR_WAIT_DEFAULT = 5 * 1000; // 5 seconds // we do want a testable, repeatable set of generated random numbers private static readonly Random s_random = new Random(5101977); // Value obtained from Dave Driver + private static int s_objectTypeCount; + private readonly int _objectID; + private readonly WaitCallback _poolCreateRequest; + private readonly PoolWaitHandles _waitHandles; + private readonly TransactedConnectionPool _transactedConnectionPool; + private readonly List _objectList; + private readonly ConcurrentStack _stackOld; + private readonly ConcurrentStack _stackNew; + private readonly ConcurrentQueue _pendingOpens; + /// + /// The private member which carries the set of authenticationcontexts for this pool (based on the user's identity). + /// + private readonly ConcurrentDictionary _pooledDbAuthenticationContexts; private readonly int _cleanupWait; private readonly DbConnectionPoolIdentity _identity; - private readonly DbConnectionFactory _connectionFactory; private readonly DbConnectionPoolGroup _connectionPoolGroup; private readonly DbConnectionPoolGroupOptions _connectionPoolGroupOptions; - private DbConnectionPoolProviderInfo _connectionPoolProviderInfo; - - /// - /// The private member which carries the set of authenticationcontexts for this pool (based on the user's identity). - /// - private readonly ConcurrentDictionary _pooledDbAuthenticationContexts; + private readonly DbConnectionPoolProviderInfo _connectionPoolProviderInfo; private State _state; - - private readonly ConcurrentStack _stackOld = new ConcurrentStack(); - private readonly ConcurrentStack _stackNew = new ConcurrentStack(); - - private readonly ConcurrentQueue _pendingOpens = new ConcurrentQueue(); private int _pendingOpensWaiting = 0; - - private readonly WaitCallback _poolCreateRequest; - private int _waitCount; - private readonly PoolWaitHandles _waitHandles; - private Exception _resError; - private volatile bool _errorOccurred; - private int _errorWait; private Timer _errorTimer; - private Timer _cleanupTimer; - - private readonly TransactedConnectionPool _transactedConnectionPool; - - private readonly List _objectList; private int _totalObjects; - private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); + private volatile bool _errorOccurred; // only created by DbConnectionPoolGroup.GetConnectionPool internal DbConnectionPool( - DbConnectionFactory connectionFactory, - DbConnectionPoolGroup connectionPoolGroup, - DbConnectionPoolIdentity identity, - DbConnectionPoolProviderInfo connectionPoolProviderInfo) + DbConnectionFactory connectionFactory, + DbConnectionPoolGroup connectionPoolGroup, + DbConnectionPoolIdentity identity, + DbConnectionPoolProviderInfo connectionPoolProviderInfo) { - Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup"); + Debug.Assert(connectionPoolGroup != null, "null connectionPoolGroup"); - if ((null != identity) && identity.IsRestricted) + if ((identity != null) && identity.IsRestricted) { throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToPoolOnRestrictedToken); } _state = State.Initializing; + _objectID = Interlocked.Increment(ref s_objectTypeCount); + _stackOld = new ConcurrentStack(); + _stackNew = new ConcurrentStack(); + _pendingOpens = new ConcurrentQueue(); lock (s_random) { // Random.Next is not thread-safe @@ -430,73 +409,58 @@ internal DbConnectionPool( _connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions; _connectionPoolProviderInfo = connectionPoolProviderInfo; _identity = identity; - _waitHandles = new PoolWaitHandles(); - _errorWait = ERROR_WAIT_DEFAULT; - _errorTimer = null; // No error yet. - + _errorTimer = null; _objectList = new List(MaxPoolSize); - - _pooledDbAuthenticationContexts = new ConcurrentDictionary(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, - capacity: 2); - - _transactedConnectionPool = new TransactedConnectionPool(this); // initialize irrespective of platform - + _pooledDbAuthenticationContexts = new ConcurrentDictionary( + concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, + capacity: 2 + ); + _transactedConnectionPool = new TransactedConnectionPool(this); _poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback _state = State.Running; + SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Constructed.", ObjectID); //_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls // StartBackgroundCallbacks after pool is actually in the collection } - private int CreationTimeout - { - get { return PoolGroupOptions.CreationTimeout; } - } + private int CreationTimeout => PoolGroupOptions.CreationTimeout; - internal int Count - { - get { return _totalObjects; } - } + internal int Count => _totalObjects; - internal DbConnectionFactory ConnectionFactory - { - get { return _connectionFactory; } - } + internal DbConnectionFactory ConnectionFactory => _connectionFactory; - internal bool ErrorOccurred - { - get { return _errorOccurred; } - } + internal bool ErrorOccurred => _errorOccurred; - private bool HasTransactionAffinity - { - get { return PoolGroupOptions.HasTransactionAffinity; } - } + private bool HasTransactionAffinity => PoolGroupOptions.HasTransactionAffinity; - internal TimeSpan LoadBalanceTimeout - { - get { return PoolGroupOptions.LoadBalanceTimeout; } - } + internal TimeSpan LoadBalanceTimeout => PoolGroupOptions.LoadBalanceTimeout; private bool NeedToReplenish { get { - if (State.Running != _state) // Don't allow connection create when not running. + if (_state != State.Running) // Don't allow connection create when not running. + { return false; + } int totalObjects = Count; if (totalObjects >= MaxPoolSize) + { return false; + } if (totalObjects < MinPoolSize) + { return true; + } - int freeObjects = (_stackNew.Count + _stackOld.Count); + int freeObjects = _stackNew.Count + _stackOld.Count; int waitingRequests = _waitCount; bool needToReplenish = (freeObjects < waitingRequests) || ((freeObjects == waitingRequests) && (totalObjects > 1)); @@ -504,69 +468,30 @@ private bool NeedToReplenish } } - internal DbConnectionPoolIdentity Identity - { - get { return _identity; } - } + internal DbConnectionPoolIdentity Identity => _identity; - internal bool IsRunning - { - get { return State.Running == _state; } - } + internal bool IsRunning => State.Running == _state; - private int MaxPoolSize - { - get { return PoolGroupOptions.MaxPoolSize; } - } + private int MaxPoolSize => PoolGroupOptions.MaxPoolSize; - private int MinPoolSize - { - get { return PoolGroupOptions.MinPoolSize; } - } + private int MinPoolSize => PoolGroupOptions.MinPoolSize; - internal int ObjectID - { - get - { - return _objectID; - } - } + internal int ObjectID => _objectID; - internal DbConnectionPoolGroup PoolGroup - { - get { return _connectionPoolGroup; } - } + internal DbConnectionPoolGroup PoolGroup => _connectionPoolGroup; - internal DbConnectionPoolGroupOptions PoolGroupOptions - { - get { return _connectionPoolGroupOptions; } - } + internal DbConnectionPoolGroupOptions PoolGroupOptions => _connectionPoolGroupOptions; - internal DbConnectionPoolProviderInfo ProviderInfo - { - get { return _connectionPoolProviderInfo; } - } + internal DbConnectionPoolProviderInfo ProviderInfo => _connectionPoolProviderInfo; /// /// Return the pooled authentication contexts. /// - internal ConcurrentDictionary AuthenticationContexts - { - get - { - return _pooledDbAuthenticationContexts; - } - } + internal ConcurrentDictionary AuthenticationContexts => _pooledDbAuthenticationContexts; - internal bool UseLoadBalancing - { - get { return PoolGroupOptions.UseLoadBalancing; } - } + internal bool UseLoadBalancing => PoolGroupOptions.UseLoadBalancing; - private bool UsingIntegrateSecurity - { - get { return (null != _identity && DbConnectionPoolIdentity.NoIdentity != _identity); } - } + private bool UsingIntegrateSecurity => _identity != null && DbConnectionPoolIdentity.s_noIdentity != _identity; private void CleanupCallback(object state) { @@ -596,9 +521,8 @@ private void CleanupCallback(object state) if (_waitHandles.PoolSemaphore.WaitOne(0)) { // We obtained a objects from the semaphore. - DbConnectionInternal obj; - if (_stackOld.TryPop(out obj)) + if (_stackOld.TryPop(out DbConnectionInternal obj)) { Debug.Assert(obj != null, "null connection is not expected"); // If we obtained one from the old stack, destroy it. @@ -655,10 +579,10 @@ private void CleanupCallback(object state) { for (; ; ) { - DbConnectionInternal obj; - - if (!_stackNew.TryPop(out obj)) + if (!_stackNew.TryPop(out DbConnectionInternal obj)) + { break; + } Debug.Assert(obj != null, "null connection is not expected"); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, ChangeStacks={1}", ObjectID, obj.ObjectID); @@ -688,7 +612,7 @@ internal void Clear() { obj = _objectList[i]; - if (null != obj) + if (obj != null) { obj.DoNotPoolThisConnection(); } @@ -715,12 +639,7 @@ internal void Clear() SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Cleared.", ObjectID); } - private Timer CreateCleanupTimer() => - ADP.UnsafeCreateTimer( - new TimerCallback(CleanupCallback), - null, - _cleanupWait, - _cleanupWait); + private Timer CreateCleanupTimer() => new Timer(new TimerCallback(CleanupCallback), null, _cleanupWait, _cleanupWait); private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) { @@ -729,7 +648,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio try { newObj = _connectionFactory.CreatePooledConnection(this, owningObject, _connectionPoolGroup.ConnectionOptions, _connectionPoolGroup.PoolKey, userOptions); - if (null == newObj) + if (newObj == null) { throw ADP.InternalError(ADP.InternalErrorCode.CreateObjectReturnedNull); // CreateObject succeeded, but null object } @@ -753,7 +672,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio // If the old connection belonged to another pool, we need to remove it from that if (oldConnection != null) { - var oldConnectionPool = oldConnection.Pool; + DbConnectionPool oldConnectionPool = oldConnection.Pool; if (oldConnectionPool != null && oldConnectionPool != this) { Debug.Assert(oldConnectionPool._state == State.ShuttingDown, "Old connections pool should be shutting down"); @@ -792,9 +711,10 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio // Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent. // timer allocation has to be done out of CER block - Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite); + Timer t = new Timer(new TimerCallback(ErrorCallback), null, Timeout.Infinite, Timeout.Infinite); bool timerIsNotDisposed; + RuntimeHelpers.PrepareConstrainedRegions(); try { } finally @@ -890,7 +810,7 @@ private void DeactivateObject(DbConnectionInternal obj) // thread. Transaction transaction = obj.EnlistedTransaction; - if (null != transaction) + if (transaction != null) { // NOTE: we're not locking on _state, so it's possible that its // value could change between the conditional check and here. @@ -995,26 +915,26 @@ private void ErrorCallback(object state) _waitHandles.ErrorEvent.Reset(); // the error state is cleaned, destroy the timer to avoid periodic invocation - Timer t = _errorTimer; + Timer timer = _errorTimer; _errorTimer = null; - if (t != null) - { - t.Dispose(); // Cancel timer request. - } + timer?.Dispose(); } private Exception TryCloneCachedException() // Cached exception can be of any type, so is not always cloneable. // This functions clones SqlException - // OleDb and Odbc connections are not passing throw this code + // OleDb and Odbc connections are not passing through this code { - if (_resError == null) + if (_resError is null) + { return null; + } - var sqlError = _resError as SqlClient.SqlException; - if (sqlError != null) + if (_resError is SqlException sqlError) + { return sqlError.InternalClone(); + } return _resError; } @@ -1027,8 +947,11 @@ private void WaitForPendingOpen() { bool started = false; + RuntimeHelpers.PrepareConstrainedRegions(); try { + + RuntimeHelpers.PrepareConstrainedRegions(); try { } finally @@ -1062,6 +985,7 @@ private void WaitForPendingOpen() bool timeout = false; Exception caughtException = null; + RuntimeHelpers.PrepareConstrainedRegions(); try { bool allowCreate = true; @@ -1114,7 +1038,9 @@ internal bool TryGetConnection(DbConnection owningObject, TaskCompletionSource= MaxPoolSize && 0 != MaxPoolSize) + if (Count >= MaxPoolSize && MaxPoolSize != 0) { if (!ReclaimEmancipatedObjects()) { @@ -1266,6 +1199,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj { if (_waitHandles.CreationSemaphore.WaitOne(unchecked((int)waitForMultipleObjectsTimeout))) { + RuntimeHelpers.PrepareConstrainedRegions(); try { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Creating new connection.", ObjectID); @@ -1302,15 +1236,15 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj } // Do not use this pooled connection if access token is about to expire soon before we can connect. - if (null != obj && obj.IsAccessTokenExpired) + if (obj != null && obj.IsAccessTokenExpired) { DestroyObject(obj); obj = null; } - } while (null == obj); + } while (obj == null); } - if (null != obj) + if (obj != null) { PrepareConnection(owningObject, obj, transaction); } @@ -1334,7 +1268,7 @@ private void PrepareConnection(DbConnection owningObject, DbConnectionInternal o { // if Activate throws an exception // put it back in the pool or have it properly disposed of - this.PutObject(obj, owningObject); + PutObject(obj, owningObject); throw; } } @@ -1365,9 +1299,8 @@ internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbCon private DbConnectionInternal GetFromGeneralPool() { - DbConnectionInternal obj = null; - if (!_stackNew.TryPop(out obj)) + if (!_stackNew.TryPop(out DbConnectionInternal obj)) { if (!_stackOld.TryPop(out obj)) { @@ -1388,12 +1321,12 @@ private DbConnectionInternal GetFromGeneralPool() // following assert to fire, which really mucks up stress against // checked bits. - if (null != obj) + if (obj != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from general pool.", ObjectID, obj.ObjectID); SqlClientEventSource.Log.ExitFreeConnection(); } - return (obj); + return obj; } private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) @@ -1401,11 +1334,11 @@ private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) transaction = ADP.GetCurrentTransaction(); DbConnectionInternal obj = null; - if (null != transaction && null != _transactedConnectionPool) + if (transaction != null && _transactedConnectionPool != null) { obj = _transactedConnectionPool.GetTransactedObject(transaction); - if (null != obj) + if (obj != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from transacted pool.", ObjectID, obj.ObjectID); SqlClientEventSource.Log.ExitFreeConnection(); @@ -1434,6 +1367,8 @@ private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) return obj; } + [ResourceExposure(ResourceScope.None)] // SxS: this method does not expose resources + [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] private void PoolCreateRequest(object state) { // called by pooler to ensure pool requests are currently being satisfied - @@ -1441,14 +1376,16 @@ private void PoolCreateRequest(object state) long scopeID = SqlClientEventSource.Log.TryPoolerScopeEnterEvent(" {0}", ObjectID); try { - if (State.Running == _state) + if (_state == State.Running) { // in case WaitForPendingOpen ever failed with no subsequent OpenAsync calls, // start it back up again if (!_pendingOpens.IsEmpty && _pendingOpensWaiting == 0) { - Thread waitOpenThread = new Thread(WaitForPendingOpen); - waitOpenThread.IsBackground = true; + Thread waitOpenThread = new Thread(WaitForPendingOpen) + { + IsBackground = true + }; waitOpenThread.Start(); } @@ -1471,8 +1408,11 @@ private void PoolCreateRequest(object state) return; } int waitResult = BOGUS_HANDLE; + + RuntimeHelpers.PrepareConstrainedRegions(); try { + RuntimeHelpers.PrepareConstrainedRegions(); try { } finally @@ -1557,7 +1497,7 @@ private void PoolCreateRequest(object state) internal void PutNewObject(DbConnectionInternal obj) { - Debug.Assert(null != obj, "why are we adding a null object to the pool?"); + Debug.Assert(obj != null, "why are we adding a null object to the pool?"); // Debug.Assert(obj.CanBePooled, "non-poolable object in pool"); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Pushing to general pool.", ObjectID, obj.ObjectID); @@ -1568,7 +1508,7 @@ internal void PutNewObject(DbConnectionInternal obj) internal void PutObject(DbConnectionInternal obj, object owningObject) { - Debug.Assert(null != obj, "null obj?"); + Debug.Assert(obj != null, "null obj"); SqlClientEventSource.Log.SoftDisconnectRequest(); // Once a connection is closing (which is the state that we're in at @@ -1593,7 +1533,7 @@ internal void PutObject(DbConnectionInternal obj, object owningObject) internal void PutObjectFromTransactedPool(DbConnectionInternal obj) { - Debug.Assert(null != obj, "null pooledObject?"); + Debug.Assert(obj != null, "null pooledObject?"); Debug.Assert(obj.EnlistedTransaction == null, "pooledObject is still enlisted?"); // called by the transacted connection pool , once it's removed the @@ -1619,7 +1559,7 @@ internal void PutObjectFromTransactedPool(DbConnectionInternal obj) private void QueuePoolCreateRequest() { - if (State.Running == _state) + if (_state == State.Running) { // Make sure we're at quota by posting a callback to the threadpool. ThreadPool.QueueUserWorkItem(_poolCreateRequest); @@ -1641,7 +1581,7 @@ private bool ReclaimEmancipatedObjects() { DbConnectionInternal obj = _objectList[i]; - if (null != obj) + if (obj != null) { bool locked = false; @@ -1708,12 +1648,9 @@ internal void Shutdown() _state = State.ShuttingDown; // deactivate timer callbacks - Timer t = _cleanupTimer; + Timer timer = _cleanupTimer; _cleanupTimer = null; - if (null != t) - { - t.Dispose(); - } + timer?.Dispose(); } // TransactionEnded merely provides the plumbing for DbConnectionInternal to access the transacted pool @@ -1722,8 +1659,8 @@ internal void Shutdown() // other objects is unnecessary (hence the asymmetry of Ended but no Begin) internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) { - Debug.Assert(null != transaction, "null transaction?"); - Debug.Assert(null != transactedObject, "null transactedObject?"); + Debug.Assert(transaction != null, "null transaction?"); + Debug.Assert(transactedObject != null, "null transactedObject?"); // Note: connection may still be associated with transaction due to Explicit Unbinding requirement. SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Transaction Completed", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); @@ -1733,7 +1670,7 @@ internal void TransactionEnded(Transaction transaction, DbConnectionInternal tra // the connection from it's list, then we put the connection back in // general circulation. TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool; - if (null != transactedConnectionPool) + if (transactedConnectionPool != null) { transactedConnectionPool.TransactionEnded(transaction, transactedObject); } @@ -1751,12 +1688,14 @@ private DbConnectionInternal UserCreateRequest(DbConnection owningObject, DbConn } else { - if ((oldConnection != null) || (Count < MaxPoolSize) || (0 == MaxPoolSize)) + if ((oldConnection != null) || (Count < MaxPoolSize) || (MaxPoolSize == 0)) { // If we have an odd number of total objects, reclaim any dead objects. // If we did not find any objects to reclaim, create a new one. if ((oldConnection != null) || (Count & 0x1) == 0x1 || !ReclaimEmancipatedObjects()) + { obj = CreateObject(owningObject, userOptions, oldConnection); + } } return obj; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 8391d18c6b..5c9b006852 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -125,6 +125,12 @@ Microsoft\Data\ProviderBase\DbConnectionPoolOptions.cs + + Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.cs + + + Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.Windows.cs + Microsoft\Data\ProviderBase\DbConnectionPoolGroupProviderInfo.cs @@ -523,6 +529,7 @@ + @@ -544,7 +551,6 @@ - @@ -642,4 +648,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbBuffer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbBuffer.cs index 30a3bc7b99..90f3ff0e34 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbBuffer.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbBuffer.cs @@ -20,369 +20,33 @@ internal abstract class DbBuffer : SafeHandle private readonly int _bufferLength; - private DbBuffer(int initialSize, bool zeroBuffer) : base(IntPtr.Zero, true) + internal DbBuffer(int initialSize) : base(IntPtr.Zero, true) { - if (0 < initialSize) + if (initialSize > 0) { - int flags = ((zeroBuffer) ? LMEM_ZEROINIT : LMEM_FIXED); - _bufferLength = initialSize; RuntimeHelpers.PrepareConstrainedRegions(); try - { } + { + } finally { - base.handle = SafeNativeMethods.LocalAlloc(flags, (IntPtr)initialSize); + handle = SafeNativeMethods.LocalAlloc(LMEM_ZEROINIT, new IntPtr(initialSize)); } - if (IntPtr.Zero == base.handle) + if (handle == IntPtr.Zero) { throw new OutOfMemoryException(); } } } - protected DbBuffer(int initialSize) : this(initialSize, true) - { - } - - protected DbBuffer(IntPtr invalidHandleValue, bool ownsHandle) : base(invalidHandleValue, ownsHandle) - { - } - - private int BaseOffset { get { return 0; } } - - public override bool IsInvalid - { - get - { - return (IntPtr.Zero == base.handle); - } - } - - internal int Length - { - get - { - return _bufferLength; - } - } - - internal string PtrToStringUni(int offset) - { - offset += BaseOffset; - Validate(offset, 2); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - - string value = null; - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - int length = UnsafeNativeMethods.lstrlenW(ptr); - Validate(offset, (2 * (length + 1))); - value = Marshal.PtrToStringUni(ptr, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - - return value; - } - - internal String PtrToStringUni(int offset, int length) - { - offset += BaseOffset; - Validate(offset, 2 * length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - - string value = null; - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - value = Marshal.PtrToStringUni(ptr, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return value; - } - - internal byte ReadByte(int offset) - { - offset += BaseOffset; - ValidateCheck(offset, 1); - Debug.Assert(0 == offset % 4, "invalid alignment"); - - byte value; - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - value = Marshal.ReadByte(ptr, offset); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return value; - } - - internal byte[] ReadBytes(int offset, int length) - { - byte[] value = new byte[length]; - return ReadBytes(offset, value, 0, length); - } - - internal byte[] ReadBytes(int offset, byte[] destination, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != destination, "null destination"); - Debug.Assert(startIndex + length <= destination.Length, "destination too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(ptr, destination, startIndex, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return destination; - } - - internal Char ReadChar(int offset) - { - short value = ReadInt16(offset); - return unchecked((char)value); - } - - internal char[] ReadChars(int offset, char[] destination, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, 2 * length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != destination, "null destination"); - Debug.Assert(startIndex + length <= destination.Length, "destination too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(ptr, destination, startIndex, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return destination; - } - - internal Double ReadDouble(int offset) - { - Int64 value = ReadInt64(offset); - return BitConverter.Int64BitsToDouble(value); - } - - internal Int16 ReadInt16(int offset) - { - offset += BaseOffset; - ValidateCheck(offset, 2); - Debug.Assert(0 == offset % 2, "invalid alignment"); - - short value; - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - value = Marshal.ReadInt16(ptr, offset); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return value; - } - - internal void ReadInt16Array(int offset, short[] destination, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, 2 * length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != destination, "null destination"); - Debug.Assert(startIndex + length <= destination.Length, "destination too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(ptr, destination, startIndex, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal Int32 ReadInt32(int offset) - { - offset += BaseOffset; - ValidateCheck(offset, 4); - Debug.Assert(0 == offset % 4, "invalid alignment"); - - int value; - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - value = Marshal.ReadInt32(ptr, offset); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return value; - } - - internal void ReadInt32Array(int offset, int[] destination, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, 4 * length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != destination, "null destination"); - Debug.Assert(startIndex + length <= destination.Length, "destination too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(ptr, destination, startIndex, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal Int64 ReadInt64(int offset) - { - offset += BaseOffset; - ValidateCheck(offset, 8); - Debug.Assert(0 == offset % IntPtr.Size, "invalid alignment"); + private int BaseOffset => 0; - long value; - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); + public override bool IsInvalid => handle == IntPtr.Zero; - IntPtr ptr = DangerousGetHandle(); - value = Marshal.ReadInt64(ptr, offset); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return value; - } + internal int Length => _bufferLength; - internal IntPtr ReadIntPtr(int offset) - { - offset += BaseOffset; - ValidateCheck(offset, IntPtr.Size); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - - IntPtr value; - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - value = Marshal.ReadIntPtr(ptr, offset); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - return value; - } - - internal unsafe Single ReadSingle(int offset) - { - Int32 value = ReadInt32(offset); - return *(Single*)&value; - } - - override protected bool ReleaseHandle() + protected override bool ReleaseHandle() { // NOTE: The SafeHandle class guarantees this will be called exactly once. IntPtr ptr = base.handle; @@ -394,236 +58,6 @@ override protected bool ReleaseHandle() return true; } - private void StructureToPtr(int offset, object structure) - { - Debug.Assert(null != structure, "null structure"); - offset += BaseOffset; - ValidateCheck(offset, Marshal.SizeOf(structure.GetType())); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.StructureToPtr(structure, ptr, false/*fDeleteOld*/); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteByte(int offset, byte value) - { - offset += BaseOffset; - ValidateCheck(offset, 1); - Debug.Assert(0 == offset % 4, "invalid alignment"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - Marshal.WriteByte(ptr, offset, value); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteBytes(int offset, byte[] source, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != source, "null source"); - Debug.Assert(startIndex + length <= source.Length, "source too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(source, startIndex, ptr, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteCharArray(int offset, char[] source, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, 2 * length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != source, "null source"); - Debug.Assert(startIndex + length <= source.Length, "source too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(source, startIndex, ptr, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteDouble(int offset, Double value) - { - WriteInt64(offset, BitConverter.DoubleToInt64Bits(value)); - } - - internal void WriteInt16(int offset, short value) - { - offset += BaseOffset; - ValidateCheck(offset, 2); - Debug.Assert(0 == offset % 2, "invalid alignment"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - Marshal.WriteInt16(ptr, offset, value); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteInt16Array(int offset, short[] source, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, 2 * length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != source, "null source"); - Debug.Assert(startIndex + length <= source.Length, "source too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(source, startIndex, ptr, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteInt32(int offset, int value) - { - offset += BaseOffset; - ValidateCheck(offset, 4); - Debug.Assert(0 == offset % 4, "invalid alignment"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - Marshal.WriteInt32(ptr, offset, value); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteInt32Array(int offset, int[] source, int startIndex, int length) - { - offset += BaseOffset; - Validate(offset, 4 * length); - Debug.Assert(0 == offset % ADP.PtrSize, "invalid alignment"); - Debug.Assert(null != source, "null source"); - Debug.Assert(startIndex + length <= source.Length, "source too small"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = ADP.IntPtrOffset(DangerousGetHandle(), offset); - Marshal.Copy(source, startIndex, ptr, length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal void WriteInt64(int offset, long value) - { - offset += BaseOffset; - ValidateCheck(offset, 8); - Debug.Assert(0 == offset % IntPtr.Size, "invalid alignment"); - - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - Marshal.WriteInt64(ptr, offset, value); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - internal void WriteIntPtr(int offset, IntPtr value) { offset += BaseOffset; @@ -648,155 +82,9 @@ internal void WriteIntPtr(int offset, IntPtr value) } } - internal unsafe void WriteSingle(int offset, Single value) - { - WriteInt32(offset, *(Int32*)&value); - } - - internal void ZeroMemory() - { - bool mustRelease = false; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - DangerousAddRef(ref mustRelease); - - IntPtr ptr = DangerousGetHandle(); - SafeNativeMethods.ZeroMemory(ptr, (IntPtr)Length); - } - finally - { - if (mustRelease) - { - DangerousRelease(); - } - } - } - - internal Guid ReadGuid(int offset) - { - // faster than Marshal.PtrToStructure(offset, typeof(Guid)) - byte[] buffer = new byte[16]; - ReadBytes(offset, buffer, 0, 16); - return new Guid(buffer); - } - internal void WriteGuid(int offset, Guid value) - { - // faster than Marshal.Copy(value.GetByteArray() - StructureToPtr(offset, value); - } - internal DateTime ReadDate(int offset) - { - short[] buffer = new short[3]; - ReadInt16Array(offset, buffer, 0, 3); - return new DateTime( - unchecked((ushort)buffer[0]), // Year - unchecked((ushort)buffer[1]), // Month - unchecked((ushort)buffer[2])); // Day - } - internal void WriteDate(int offset, DateTime value) - { - short[] buffer = new short[3] { - unchecked((short)value.Year), - unchecked((short)value.Month), - unchecked((short)value.Day), - }; - WriteInt16Array(offset, buffer, 0, 3); - } - - internal TimeSpan ReadTime(int offset) - { - short[] buffer = new short[3]; - ReadInt16Array(offset, buffer, 0, 3); - return new TimeSpan( - unchecked((ushort)buffer[0]), // Hours - unchecked((ushort)buffer[1]), // Minutes - unchecked((ushort)buffer[2])); // Seconds - } - internal void WriteTime(int offset, TimeSpan value) - { - short[] buffer = new short[3] { - unchecked((short)value.Hours), - unchecked((short)value.Minutes), - unchecked((short)value.Seconds), - }; - WriteInt16Array(offset, buffer, 0, 3); - } - - internal DateTime ReadDateTime(int offset) - { - short[] buffer = new short[6]; - ReadInt16Array(offset, buffer, 0, 6); - int ticks = ReadInt32(offset + 12); - DateTime value = new DateTime( - unchecked((ushort)buffer[0]), // Year - unchecked((ushort)buffer[1]), // Month - unchecked((ushort)buffer[2]), // Day - unchecked((ushort)buffer[3]), // Hours - unchecked((ushort)buffer[4]), // Minutes - unchecked((ushort)buffer[5])); // Seconds - return value.AddTicks(ticks / 100); - } - internal void WriteDateTime(int offset, DateTime value) - { - int ticks = (int)(value.Ticks % 10000000L) * 100; - short[] buffer = new short[6] { - unchecked((short)value.Year), - unchecked((short)value.Month), - unchecked((short)value.Day), - unchecked((short)value.Hour), - unchecked((short)value.Minute), - unchecked((short)value.Second), - }; - WriteInt16Array(offset, buffer, 0, 6); - WriteInt32(offset + 12, ticks); - } - - internal Decimal ReadNumeric(int offset) - { - byte[] bits = new byte[20]; - ReadBytes(offset, bits, 1, 19); - - int[] buffer = new int[4]; - buffer[3] = ((int)bits[2]) << 16; // scale - if (0 == bits[3]) - { - buffer[3] |= unchecked((int)0x80000000); //sign - } - buffer[0] = BitConverter.ToInt32(bits, 4); // low - buffer[1] = BitConverter.ToInt32(bits, 8); // mid - buffer[2] = BitConverter.ToInt32(bits, 12); // high - if (0 != BitConverter.ToInt32(bits, 16)) - { - throw ADP.NumericToDecimalOverflow(); - } - return new Decimal(buffer); - } - - internal void WriteNumeric(int offset, Decimal value, byte precision) - { - int[] tmp = Decimal.GetBits(value); - byte[] buffer = new byte[20]; - - buffer[1] = precision; - Buffer.BlockCopy(tmp, 14, buffer, 2, 2); // copy sign and scale - buffer[3] = (Byte)((0 == buffer[3]) ? 1 : 0); // flip sign for native - Buffer.BlockCopy(tmp, 0, buffer, 4, 12); - buffer[16] = 0; - buffer[17] = 0; - buffer[18] = 0; - buffer[19] = 0; - WriteBytes(offset, buffer, 1, 19); - } - - [ConditionalAttribute("DEBUG")] + [Conditional("DEBUG")] protected void ValidateCheck(int offset, int count) - { - Validate(offset, count); - } - - protected void Validate(int offset, int count) { if ((offset < 0) || (count < 0) || (Length < checked(offset + count))) { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs index 8d04056add..453621a396 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPool.cs @@ -2,27 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data.Common; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading; +using System.Threading.Tasks; +using System.Transactions; +using Microsoft.Data.Common; +using Microsoft.Data.SqlClient; + namespace Microsoft.Data.ProviderBase { - - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Data.Common; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.CompilerServices; - using System.Runtime.ConstrainedExecution; - using System.Runtime.InteropServices; - using System.Runtime.Versioning; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.Data.Common; - using Microsoft.Data.SqlClient; - - using SysTx = System.Transactions; - - sealed internal class DbConnectionPool + internal sealed partial class DbConnectionPool { private enum State { @@ -34,24 +32,20 @@ private enum State // This class is a way to stash our cloned Tx key for later disposal when it's no longer needed. // We can't get at the key in the dictionary without enumerating entries, so we stash an extra // copy as part of the value. - sealed private class TransactedConnectionList : List + private sealed class TransactedConnectionList : List { - private SysTx.Transaction _transaction; - internal TransactedConnectionList(int initialAllocation, SysTx.Transaction tx) : base(initialAllocation) + private readonly Transaction _transaction; + + internal TransactedConnectionList(int initialAllocation, Transaction tx) + : base(initialAllocation) { _transaction = tx; } - internal void Dispose() - { - if (null != _transaction) - { - _transaction.Dispose(); - } - } + internal void Dispose() => _transaction?.Dispose(); } - sealed class PendingGetConnection + private sealed class PendingGetConnection { public PendingGetConnection(long dueTime, DbConnection owner, TaskCompletionSource completion, DbConnectionOptions userOptions) { @@ -60,49 +54,37 @@ public PendingGetConnection(long dueTime, DbConnection owner, TaskCompletionSour Completion = completion; UserOptions = userOptions; } + public long DueTime { get; private set; } public DbConnection Owner { get; private set; } public TaskCompletionSource Completion { get; private set; } public DbConnectionOptions UserOptions { get; private set; } } - sealed private class TransactedConnectionPool + private sealed class TransactedConnectionPool { + private static int s_objectTypeCount; - Dictionary _transactedCxns; - - DbConnectionPool _pool; - - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); + private readonly Dictionary _transactedCxns; + private readonly DbConnectionPool _pool; + private readonly int _objectID; internal TransactedConnectionPool(DbConnectionPool pool) { - Debug.Assert(null != pool, "null pool?"); + Debug.Assert(pool != null, "null pool?"); + _objectID = Interlocked.Increment(ref s_objectTypeCount); _pool = pool; - _transactedCxns = new Dictionary(); + _transactedCxns = new Dictionary(); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Constructed for connection pool {1}", ObjectID, _pool.ObjectID); } - internal int ObjectID - { - get - { - return _objectID; - } - } + internal int ObjectID => _objectID; - internal DbConnectionPool Pool - { - get - { - return _pool; - } - } + internal DbConnectionPool Pool => _pool; - internal DbConnectionInternal GetTransactedObject(SysTx.Transaction transaction) + internal DbConnectionInternal GetTransactedObject(Transaction transaction) { - Debug.Assert(null != transaction, "null transaction?"); + Debug.Assert(transaction != null, "null transaction?"); DbConnectionInternal transactedObject = null; @@ -138,17 +120,17 @@ internal DbConnectionInternal GetTransactedObject(SysTx.Transaction transaction) } } - if (null != transactedObject) + if (transactedObject != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Popped.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); } return transactedObject; } - internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInternal transactedObject) + internal void PutTransactedObject(Transaction transaction, DbConnectionInternal transactedObject) { - Debug.Assert(null != transaction, "null transaction?"); - Debug.Assert(null != transactedObject, "null transactedObject?"); + Debug.Assert(transaction != null, "null transaction?"); + Debug.Assert(transactedObject != null, "null transactedObject?"); TransactedConnectionList connections; bool txnFound = false; @@ -179,7 +161,7 @@ internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInt { // create the transacted pool, making sure to clone the associated transaction // for use as a key in our internal dictionary of transactions and connections - SysTx.Transaction transactionClone = null; + Transaction transactionClone = null; TransactedConnectionList newConnections = null; try @@ -220,7 +202,7 @@ internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInt } finally { - if (null != transactionClone) + if (transactionClone != null) { if (newConnections != null) { @@ -243,10 +225,9 @@ internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInt } - internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) + internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Transaction Completed", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); - TransactedConnectionList connections; int entry = -1; // NOTE: because TransactionEnded is an asynchronous notification, there's no guarantee @@ -259,7 +240,7 @@ internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionIntern lock (_transactedCxns) { - if (_transactedCxns.TryGetValue(transaction, out connections)) + if (_transactedCxns.TryGetValue(transaction, out TransactedConnectionList connections)) { Debug.Assert(connections != null); @@ -295,7 +276,6 @@ internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionIntern } else { - //Debug.Assert ( false, "TransactionCompletedEvent fired before PutTransactedObject put the connection in the transacted pool." ); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Transacted pool not yet created prior to transaction completing. Connection may be leaked.", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); } } @@ -313,7 +293,6 @@ internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionIntern private sealed class PoolWaitHandles : DbBuffer { - private readonly Semaphore _poolSemaphore; private readonly ManualResetEvent _errorEvent; @@ -350,9 +329,9 @@ internal PoolWaitHandles() : base(3 * IntPtr.Size) _errorHandle.DangerousAddRef(ref mustRelease2); _creationHandle.DangerousAddRef(ref mustRelease3); - Debug.Assert(0 == SEMAPHORE_HANDLE, "SEMAPHORE_HANDLE"); - Debug.Assert(1 == ERROR_HANDLE, "ERROR_HANDLE"); - Debug.Assert(2 == CREATION_HANDLE, "CREATION_HANDLE"); + Debug.Assert(SEMAPHORE_HANDLE == 0, "SEMAPHORE_HANDLE must be 0"); + Debug.Assert(ERROR_HANDLE == 1, "ERROR_HANDLE must be 1"); + Debug.Assert(CREATION_HANDLE == 2, "CREATION_HANDLE must be 2"); WriteIntPtr(SEMAPHORE_HANDLE * IntPtr.Size, _poolHandle.DangerousGetHandle()); WriteIntPtr(ERROR_HANDLE * IntPtr.Size, _errorHandle.DangerousGetHandle()); @@ -400,15 +379,15 @@ protected override bool ReleaseHandle() { // NOTE: The SafeHandle class guarantees this will be called exactly once. // we know we can touch these other managed objects because of our original DangerousAddRef - if (0 != (1 & _releaseFlags)) + if ((1 & _releaseFlags) != 0) { _poolHandle.DangerousRelease(); } - if (0 != (2 & _releaseFlags)) + if ((2 & _releaseFlags) != 0) { _errorHandle.DangerousRelease(); } - if (0 != (4 & _releaseFlags)) + if ((4 & _releaseFlags) != 0) { _creationHandle.DangerousRelease(); } @@ -416,88 +395,80 @@ protected override bool ReleaseHandle() } } - private const int MAX_Q_SIZE = (int)0x00100000; + private const int MAX_Q_SIZE = 0x00100000; // The order of these is important; we want the WaitAny call to be signaled // for a free object before a creation signal. Only the index first signaled // object is returned from the WaitAny call. - private const int SEMAPHORE_HANDLE = (int)0x0; - private const int ERROR_HANDLE = (int)0x1; - private const int CREATION_HANDLE = (int)0x2; - private const int BOGUS_HANDLE = (int)0x3; + private const int SEMAPHORE_HANDLE = 0x0; + private const int ERROR_HANDLE = 0x1; + private const int CREATION_HANDLE = 0x2; + private const int BOGUS_HANDLE = 0x3; private const int WAIT_OBJECT_0 = 0; - private const int WAIT_TIMEOUT = (int)0x102; - private const int WAIT_ABANDONED = (int)0x80; + private const int WAIT_TIMEOUT = 0x102; + private const int WAIT_ABANDONED = 0x80; private const int WAIT_FAILED = -1; private const int ERROR_WAIT_DEFAULT = 5 * 1000; // 5 seconds // we do want a testable, repeatable set of generated random numbers - private static readonly Random _random = new Random(5101977); // Value obtained from Dave Driver + private static readonly Random s_random = new Random(5101977); // Value obtained from Dave Driver + private static int s_objectTypeCount; + private readonly int _objectID; + private readonly WaitCallback _poolCreateRequest; + private readonly PoolWaitHandles _waitHandles; + private readonly TransactedConnectionPool _transactedConnectionPool; + private readonly List _objectList; + private readonly ConcurrentStack _stackOld; + private readonly ConcurrentStack _stackNew; + private readonly ConcurrentQueue _pendingOpens; + /// + /// The private member which carries the set of authenticationcontexts for this pool (based on the user's identity). + /// + private readonly ConcurrentDictionary _pooledDbAuthenticationContexts; private readonly int _cleanupWait; private readonly DbConnectionPoolIdentity _identity; - private readonly DbConnectionFactory _connectionFactory; private readonly DbConnectionPoolGroup _connectionPoolGroup; private readonly DbConnectionPoolGroupOptions _connectionPoolGroupOptions; - private DbConnectionPoolProviderInfo _connectionPoolProviderInfo; - - /// - /// The private member which carries the set of authenticationcontexts for this pool (based on the user's identity). - /// - private readonly ConcurrentDictionary _pooledDbAuthenticationContexts; + private readonly DbConnectionPoolProviderInfo _connectionPoolProviderInfo; private State _state; - - private readonly ConcurrentStack _stackOld = new ConcurrentStack(); - private readonly ConcurrentStack _stackNew = new ConcurrentStack(); - - private readonly ConcurrentQueue _pendingOpens = new ConcurrentQueue(); private int _pendingOpensWaiting = 0; - - private readonly WaitCallback _poolCreateRequest; - private int _waitCount; - private readonly PoolWaitHandles _waitHandles; - private Exception _resError; - private volatile bool _errorOccurred; - private int _errorWait; private Timer _errorTimer; - private Timer _cleanupTimer; - - private readonly TransactedConnectionPool _transactedConnectionPool; - - private readonly List _objectList; private int _totalObjects; - private static int _objectTypeCount; // EventSource counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); + private volatile bool _errorOccurred; // only created by DbConnectionPoolGroup.GetConnectionPool internal DbConnectionPool( - DbConnectionFactory connectionFactory, - DbConnectionPoolGroup connectionPoolGroup, - DbConnectionPoolIdentity identity, - DbConnectionPoolProviderInfo connectionPoolProviderInfo) + DbConnectionFactory connectionFactory, + DbConnectionPoolGroup connectionPoolGroup, + DbConnectionPoolIdentity identity, + DbConnectionPoolProviderInfo connectionPoolProviderInfo) { - Debug.Assert(ADP.IsWindowsNT, "Attempting to construct a connection pool on Win9x?"); - Debug.Assert(null != connectionPoolGroup, "null connectionPoolGroup"); + Debug.Assert(connectionPoolGroup != null, "null connectionPoolGroup"); - if ((null != identity) && identity.IsRestricted) + if ((identity != null) && identity.IsRestricted) { throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToPoolOnRestrictedToken); } _state = State.Initializing; + _objectID = Interlocked.Increment(ref s_objectTypeCount); + _stackOld = new ConcurrentStack(); + _stackNew = new ConcurrentStack(); + _pendingOpens = new ConcurrentQueue(); - lock (_random) + lock (s_random) { // Random.Next is not thread-safe - _cleanupWait = _random.Next(12, 24) * 10 * 1000; // 2-4 minutes in 10 sec intervals, WebData 103603 + _cleanupWait = s_random.Next(12, 24) * 10 * 1000; // 2-4 minutes in 10 sec intervals } _connectionFactory = connectionFactory; @@ -505,76 +476,58 @@ internal DbConnectionPool( _connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions; _connectionPoolProviderInfo = connectionPoolProviderInfo; _identity = identity; - _waitHandles = new PoolWaitHandles(); - _errorWait = ERROR_WAIT_DEFAULT; - _errorTimer = null; // No error yet. - + _errorTimer = null; _objectList = new List(MaxPoolSize); - - _pooledDbAuthenticationContexts = new ConcurrentDictionary(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, - capacity: 2); - - if (ADP.IsPlatformNT5) - { - _transactedConnectionPool = new TransactedConnectionPool(this); - } - + _pooledDbAuthenticationContexts = new ConcurrentDictionary( + concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, + capacity: 2 + ); + _transactedConnectionPool = new TransactedConnectionPool(this); _poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback _state = State.Running; + SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Constructed.", ObjectID); //_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls // StartBackgroundCallbacks after pool is actually in the collection } - private int CreationTimeout - { - get { return PoolGroupOptions.CreationTimeout; } - } + private int CreationTimeout => PoolGroupOptions.CreationTimeout; - internal int Count - { - get { return _totalObjects; } - } + internal int Count => _totalObjects; - internal DbConnectionFactory ConnectionFactory - { - get { return _connectionFactory; } - } + internal DbConnectionFactory ConnectionFactory => _connectionFactory; - internal bool ErrorOccurred - { - get { return _errorOccurred; } - } + internal bool ErrorOccurred => _errorOccurred; - private bool HasTransactionAffinity - { - get { return PoolGroupOptions.HasTransactionAffinity; } - } + private bool HasTransactionAffinity => PoolGroupOptions.HasTransactionAffinity; - internal TimeSpan LoadBalanceTimeout - { - get { return PoolGroupOptions.LoadBalanceTimeout; } - } + internal TimeSpan LoadBalanceTimeout => PoolGroupOptions.LoadBalanceTimeout; private bool NeedToReplenish { get { - if (State.Running != _state) // SQL BU DT 364595 - don't allow connection create when not running. + if (_state != State.Running) // Don't allow connection create when not running. + { return false; + } int totalObjects = Count; if (totalObjects >= MaxPoolSize) + { return false; + } if (totalObjects < MinPoolSize) + { return true; + } - int freeObjects = (_stackNew.Count + _stackOld.Count); + int freeObjects = _stackNew.Count + _stackOld.Count; int waitingRequests = _waitCount; bool needToReplenish = (freeObjects < waitingRequests) || ((freeObjects == waitingRequests) && (totalObjects > 1)); @@ -582,76 +535,34 @@ private bool NeedToReplenish } } - internal DbConnectionPoolIdentity Identity - { - get { return _identity; } - } + internal DbConnectionPoolIdentity Identity => _identity; - internal bool IsRunning - { - get { return State.Running == _state; } - } + internal bool IsRunning => State.Running == _state; - private int MaxPoolSize - { - get { return PoolGroupOptions.MaxPoolSize; } - } + private int MaxPoolSize => PoolGroupOptions.MaxPoolSize; - private int MinPoolSize - { - get { return PoolGroupOptions.MinPoolSize; } - } + private int MinPoolSize => PoolGroupOptions.MinPoolSize; - internal int ObjectID - { - get - { - return _objectID; - } - } + internal int ObjectID => _objectID; - internal DbConnectionPoolCounters PerformanceCounters - { - get { return _connectionFactory.PerformanceCounters; } - } + internal DbConnectionPoolCounters PerformanceCounters => _connectionFactory.PerformanceCounters; - internal DbConnectionPoolGroup PoolGroup - { - get { return _connectionPoolGroup; } - } + internal DbConnectionPoolGroup PoolGroup => _connectionPoolGroup; - internal DbConnectionPoolGroupOptions PoolGroupOptions - { - get { return _connectionPoolGroupOptions; } - } + internal DbConnectionPoolGroupOptions PoolGroupOptions => _connectionPoolGroupOptions; - internal DbConnectionPoolProviderInfo ProviderInfo - { - get { return _connectionPoolProviderInfo; } - } + internal DbConnectionPoolProviderInfo ProviderInfo => _connectionPoolProviderInfo; /// /// Return the pooled authentication contexts. /// - internal ConcurrentDictionary AuthenticationContexts - { - get - { - return _pooledDbAuthenticationContexts; - } - } + internal ConcurrentDictionary AuthenticationContexts => _pooledDbAuthenticationContexts; - internal bool UseLoadBalancing - { - get { return PoolGroupOptions.UseLoadBalancing; } - } + internal bool UseLoadBalancing => PoolGroupOptions.UseLoadBalancing; - private bool UsingIntegrateSecurity - { - get { return (null != _identity && DbConnectionPoolIdentity.NoIdentity != _identity); } - } + private bool UsingIntegrateSecurity => _identity != null && DbConnectionPoolIdentity.s_noIdentity != _identity; - private void CleanupCallback(Object state) + private void CleanupCallback(object state) { // Called when the cleanup-timer ticks over. @@ -680,9 +591,8 @@ private void CleanupCallback(Object state) if (_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) { // We obtained a objects from the semaphore. - DbConnectionInternal obj; - if (_stackOld.TryPop(out obj)) + if (_stackOld.TryPop(out DbConnectionInternal obj)) { Debug.Assert(obj != null, "null connection is not expected"); // If we obtained one from the old stack, destroy it. @@ -739,10 +649,10 @@ private void CleanupCallback(Object state) { for (; ; ) { - DbConnectionInternal obj; - - if (!_stackNew.TryPop(out obj)) + if (!_stackNew.TryPop(out DbConnectionInternal obj)) + { break; + } Debug.Assert(obj != null, "null connection is not expected"); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, ChangeStacks={1}", ObjectID, obj.ObjectID); @@ -772,7 +682,7 @@ internal void Clear() { obj = _objectList[i]; - if (null != obj) + if (obj != null) { obj.DoNotPoolThisConnection(); } @@ -799,10 +709,7 @@ internal void Clear() SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Cleared.", ObjectID); } - private Timer CreateCleanupTimer() - { - return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait)); - } + private Timer CreateCleanupTimer() => new Timer(new TimerCallback(CleanupCallback), null, _cleanupWait, _cleanupWait); private bool IsBlockingPeriodEnabled() { @@ -812,35 +719,24 @@ private bool IsBlockingPeriodEnabled() return true; } - var policy = poolGroupConnectionOptions.PoolBlockingPeriod; - - switch (policy) + switch (poolGroupConnectionOptions.PoolBlockingPeriod) { case PoolBlockingPeriod.Auto: + if (ADP.IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource)) { - if (ADP.IsAzureSqlServerEndpoint(poolGroupConnectionOptions.DataSource)) - { - return false; // in Azure it will be Disabled - } - else - { - return true; // in Non Azure, it will be Enabled - } + return false; // in Azure it will be Disabled } - case PoolBlockingPeriod.AlwaysBlock: + else { - return true; //Enabled + return true; // in Non Azure, it will be Enabled } + case PoolBlockingPeriod.AlwaysBlock: + return true; case PoolBlockingPeriod.NeverBlock: - { - return false; //Disabled - } + return false; default: - { - //we should never get into this path. - Debug.Fail("Unknown PoolBlockingPeriod. Please specify explicit results in above switch case statement."); - return true; - } + Debug.Fail("Unknown PoolBlockingPeriod. Please specify explicit results in above switch case statement."); + return true; } } @@ -851,7 +747,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio try { newObj = _connectionFactory.CreatePooledConnection(this, owningObject, _connectionPoolGroup.ConnectionOptions, _connectionPoolGroup.PoolKey, userOptions); - if (null == newObj) + if (newObj == null) { throw ADP.InternalError(ADP.InternalErrorCode.CreateObjectReturnedNull); // CreateObject succeeded, but null object } @@ -875,7 +771,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio // If the old connection belonged to another pool, we need to remove it from that if (oldConnection != null) { - var oldConnectionPool = oldConnection.Pool; + DbConnectionPool oldConnectionPool = oldConnection.Pool; if (oldConnectionPool != null && oldConnectionPool != this) { Debug.Assert(oldConnectionPool._state == State.ShuttingDown, "Old connections pool should be shutting down"); @@ -893,7 +789,6 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio } catch (Exception e) { - // UNDONE - should not be catching all exceptions!!! if (!ADP.IsCatchableExceptionType(e)) { throw; @@ -917,10 +812,11 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio // Failed to create instance _resError = e; - // VSTFDEVDIV 479561: Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent. + // Make sure the timer starts even if ThreadAbort occurs after setting the ErrorEvent. // timer allocation has to be done out of CER block - Timer t = new Timer(new TimerCallback(this.ErrorCallback), null, Timeout.Infinite, Timeout.Infinite); + Timer t = new Timer(new TimerCallback(ErrorCallback), null, Timeout.Infinite, Timeout.Infinite); + bool timerIsNotDisposed; RuntimeHelpers.PrepareConstrainedRegions(); try @@ -955,7 +851,7 @@ private DbConnectionInternal CreateObject(DbConnection owningObject, DbConnectio private void DeactivateObject(DbConnectionInternal obj) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Deactivating.", ObjectID, obj.ObjectID); - obj.DeactivateConnection(); // we presume this operation is safe outside of a lock... + obj.DeactivateConnection(); bool returnToGeneralPool = false; bool destroyObject = false; @@ -1014,8 +910,8 @@ private void DeactivateObject(DbConnectionInternal obj) // the transaction asynchronously completing on a second // thread. - SysTx.Transaction transaction = obj.EnlistedTransaction; - if (null != transaction) + Transaction transaction = obj.EnlistedTransaction; + if (transaction != null) { // NOTE: we're not locking on _state, so it's possible that its // value could change between the conditional check and here. @@ -1069,9 +965,6 @@ private void DeactivateObject(DbConnectionInternal obj) } else if (destroyObject) { - // VSTFDEVDIV# 479556 - connections that have been marked as no longer - // poolable (e.g. exceeded their connection lifetime) are not, in fact, - // returned to the general pool DestroyObject(obj); QueuePoolCreateRequest(); } @@ -1081,8 +974,6 @@ private void DeactivateObject(DbConnectionInternal obj) // ensure that the connection was processed Debug.Assert(rootTxn == true || returnToGeneralPool == true || destroyObject == true); - - // TODO: BID trace processing state? } internal void DestroyObject(DbConnectionInternal obj) @@ -1118,39 +1009,39 @@ internal void DestroyObject(DbConnectionInternal obj) } } - private void ErrorCallback(Object state) + private void ErrorCallback(object state) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Resetting Error handling.", ObjectID); _errorOccurred = false; _waitHandles.ErrorEvent.Reset(); // the error state is cleaned, destroy the timer to avoid periodic invocation - Timer t = _errorTimer; + Timer timer = _errorTimer; _errorTimer = null; - if (t != null) - { - t.Dispose(); // Cancel timer request. - } + timer?.Dispose(); } private Exception TryCloneCachedException() // Cached exception can be of any type, so is not always cloneable. // This functions clones SqlException - // OleDb and Odbc connections are not passing throw this code + // OleDb and Odbc connections are not passing through this code { - if (_resError == null) + if (_resError is null) + { return null; - if (_resError.GetType() == typeof(SqlClient.SqlException)) - return ((SqlClient.SqlException)_resError).InternalClone(); + } + + if (_resError is SqlException sqlError) + { + return sqlError.InternalClone(); + } + return _resError; } - void WaitForPendingOpen() + private void WaitForPendingOpen() { - - Debug.Assert(!Thread.CurrentThread.IsThreadPoolThread, "This thread may block for a long time. Threadpool threads should not be used."); - PendingGetConnection next; do @@ -1176,7 +1067,6 @@ void WaitForPendingOpen() while (_pendingOpens.TryDequeue(out next)) { - if (next.Completion.Task.IsCompleted) { continue; @@ -1222,22 +1112,28 @@ void WaitForPendingOpen() } #endif //DEBUG } - catch (System.OutOfMemoryException) + catch (OutOfMemoryException) { if (connection != null) - { connection.DoomThisConnection(); } + { + connection.DoomThisConnection(); + } throw; } - catch (System.StackOverflowException) + catch (StackOverflowException) { if (connection != null) - { connection.DoomThisConnection(); } + { + connection.DoomThisConnection(); + } throw; } - catch (System.Threading.ThreadAbortException) + catch (ThreadAbortException) { if (connection != null) - { connection.DoomThisConnection(); } + { + connection.DoomThisConnection(); + } throw; } catch (Exception e) @@ -1271,13 +1167,11 @@ void WaitForPendingOpen() Interlocked.Exchange(ref _pendingOpensWaiting, 0); } } - } while (_pendingOpens.TryPeek(out next)); } internal bool TryGetConnection(DbConnection owningObject, TaskCompletionSource retry, DbConnectionOptions userOptions, out DbConnectionInternal connection) { - uint waitForMultipleObjectsTimeout = 0; bool allowCreate = false; @@ -1285,9 +1179,11 @@ internal bool TryGetConnection(DbConnection owningObject, TaskCompletionSource {0}, Getting connection.", ObjectID); @@ -1348,7 +1245,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj obj = GetFromTransactedPool(out transaction); } - if (null == obj) + if (obj == null) { Interlocked.Increment(ref _waitCount); uint waitHandleCount = allowCreate ? 3u : 2u; @@ -1374,7 +1271,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj } finally { - waitResult = SafeNativeMethods.WaitForMultipleObjectsEx(waitHandleCount, _waitHandles.DangerousGetHandle(), false, waitForMultipleObjectsTimeout, false); + waitResult = Common.SafeNativeMethods.WaitForMultipleObjectsEx(waitHandleCount, _waitHandles.DangerousGetHandle(), false, waitForMultipleObjectsTimeout, false); // VSTFDEVDIV 479551 - call GetHRForLastWin32Error immediately after after the native call if (waitResult == WAIT_FAILED) @@ -1398,7 +1295,6 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj return false; case ERROR_HANDLE: - // Throw the error that PoolCreateRequest stashed. SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Errors are set.", ObjectID); Interlocked.Decrement(ref _waitCount); @@ -1412,7 +1308,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj } catch { - if (null == obj) + if (obj == null) { Interlocked.Decrement(ref _waitCount); } @@ -1420,24 +1316,21 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj } finally { - // SQLBUDT #386664 - ensure that we release this waiter, regardless + // Ensure that we release this waiter, regardless // of any exceptions that may be thrown. - if (null != obj) + if (obj != null) { Interlocked.Decrement(ref _waitCount); } } - if (null == obj) + if (obj == null) { // If we were not able to create an object, check to see if // we reached MaxPoolSize. If so, we will no longer wait on // the CreationHandle, but instead wait for a free object or // the timeout. - - // BUG - if we receive the CreationHandle midway into the wait - // period and re-wait, we will be waiting on the full period - if (Count >= MaxPoolSize && 0 != MaxPoolSize) + if (Count >= MaxPoolSize && MaxPoolSize != 0) { if (!ReclaimEmancipatedObjects()) { @@ -1495,17 +1388,17 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj Marshal.ThrowExceptionForHR(waitForMultipleObjectsExHR); goto default; // if ThrowExceptionForHR didn't throw for some reason - case (WAIT_ABANDONED + SEMAPHORE_HANDLE): + case WAIT_ABANDONED + SEMAPHORE_HANDLE: SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Semaphore handle abandonded.", ObjectID); Interlocked.Decrement(ref _waitCount); throw new AbandonedMutexException(SEMAPHORE_HANDLE, _waitHandles.PoolSemaphore); - case (WAIT_ABANDONED + ERROR_HANDLE): + case WAIT_ABANDONED + ERROR_HANDLE: SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Error handle abandonded.", ObjectID); Interlocked.Decrement(ref _waitCount); throw new AbandonedMutexException(ERROR_HANDLE, _waitHandles.ErrorEvent); - case (WAIT_ABANDONED + CREATION_HANDLE): + case WAIT_ABANDONED + CREATION_HANDLE: SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Creation handle abandoned.", ObjectID); Interlocked.Decrement(ref _waitCount); throw new AbandonedMutexException(CREATION_HANDLE, _waitHandles.CreationSemaphore); @@ -1520,8 +1413,8 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj { if (CREATION_HANDLE == waitResult) { - int result = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero); - if (0 == result) + int result = Common.SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero); + if (result == 0) { // failure case releaseSemaphoreResult = Marshal.GetHRForLastWin32Error(); } @@ -1531,21 +1424,21 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj _waitHandles.DangerousRelease(); } } - if (0 != releaseSemaphoreResult) + if (releaseSemaphoreResult != 0) { Marshal.ThrowExceptionForHR(releaseSemaphoreResult); // will only throw if (hresult < 0) } // Do not use this pooled connection if access token is about to expire soon before we can connect. - if (null != obj && obj.IsAccessTokenExpired) + if (obj != null && obj.IsAccessTokenExpired) { DestroyObject(obj); obj = null; } - } while (null == obj); + } while (obj == null); } - if (null != obj) + if (obj != null) { PrepareConnection(owningObject, obj, transaction); } @@ -1554,7 +1447,7 @@ private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObj return true; } - private void PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, SysTx.Transaction transaction) + private void PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, Transaction transaction) { lock (obj) { // Protect against Clear and ReclaimEmancipatedObjects, which call IsEmancipated, which is affected by PrePush and PostPop @@ -1568,7 +1461,7 @@ private void PrepareConnection(DbConnection owningObject, DbConnectionInternal o { // if Activate throws an exception // put it back in the pool or have it properly disposed of - this.PutObject(obj, owningObject); + PutObject(obj, owningObject); throw; } } @@ -1599,9 +1492,8 @@ internal DbConnectionInternal ReplaceConnection(DbConnection owningObject, DbCon private DbConnectionInternal GetFromGeneralPool() { - DbConnectionInternal obj = null; - if (!_stackNew.TryPop(out obj)) + if (!_stackNew.TryPop(out DbConnectionInternal obj)) { if (!_stackOld.TryPop(out obj)) { @@ -1617,30 +1509,29 @@ private DbConnectionInternal GetFromGeneralPool() Debug.Assert(obj != null, "null connection is not expected"); } - // SQLBUDT #356870 -- When another thread is clearing this pool, + // When another thread is clearing this pool, // it will remove all connections in this pool which causes the // following assert to fire, which really mucks up stress against - // checked bits. The assert is benign, so we're commenting it out. - //Debug.Assert(obj != null, "GetFromGeneralPool called with nothing in the pool!"); + // checked bits. - if (null != obj) + if (obj != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from general pool.", ObjectID, obj.ObjectID); PerformanceCounters.NumberOfFreeConnections.Decrement(); } - return (obj); + return obj; } - private DbConnectionInternal GetFromTransactedPool(out SysTx.Transaction transaction) + private DbConnectionInternal GetFromTransactedPool(out Transaction transaction) { transaction = ADP.GetCurrentTransaction(); DbConnectionInternal obj = null; - if (null != transaction && null != _transactedConnectionPool) + if (transaction != null && _transactedConnectionPool != null) { obj = _transactedConnectionPool.GetTransactedObject(transaction); - if (null != obj) + if (obj != null) { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Popped from transacted pool.", ObjectID, obj.ObjectID); PerformanceCounters.NumberOfFreeConnections.Decrement(); @@ -1678,15 +1569,16 @@ private void PoolCreateRequest(object state) long scopeID = SqlClientEventSource.Log.TryPoolerScopeEnterEvent(" {0}", ObjectID); try { - if (State.Running == _state) + if (_state == State.Running) { - // in case WaitForPendingOpen ever failed with no subsequent OpenAsync calls, // start it back up again if (!_pendingOpens.IsEmpty && _pendingOpensWaiting == 0) { - Thread waitOpenThread = new Thread(WaitForPendingOpen); - waitOpenThread.IsBackground = true; + Thread waitOpenThread = new Thread(WaitForPendingOpen) + { + IsBackground = true + }; waitOpenThread.Start(); } @@ -1724,7 +1616,7 @@ private void PoolCreateRequest(object state) { } finally { - waitResult = SafeNativeMethods.WaitForSingleObjectEx(_waitHandles.CreationHandle.DangerousGetHandle(), timeout, false); + waitResult = Common.SafeNativeMethods.WaitForSingleObjectEx(_waitHandles.CreationHandle.DangerousGetHandle(), timeout, false); } if (WAIT_OBJECT_0 == waitResult) { @@ -1740,7 +1632,7 @@ private void PoolCreateRequest(object state) // We do not need to check error flag here, since we know if // CreateObject returned null, we are in error case. - if (null != newObj) + if (newObj != null) { PutNewObject(newObj); } @@ -1759,13 +1651,11 @@ private void PoolCreateRequest(object state) } else { - // trace waitResult and ignore the failure SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, PoolCreateRequest called WaitForSingleObject failed {1}", ObjectID, waitResult); } } catch (Exception e) { - // UNDONE - should not be catching all exceptions!!! if (!ADP.IsCatchableExceptionType(e)) { throw; @@ -1781,7 +1671,7 @@ private void PoolCreateRequest(object state) if (WAIT_OBJECT_0 == waitResult) { // reuse waitResult and ignore its value - waitResult = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero); + waitResult = Common.SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero); } if (mustRelease) { @@ -1800,12 +1690,7 @@ private void PoolCreateRequest(object state) internal void PutNewObject(DbConnectionInternal obj) { - Debug.Assert(null != obj, "why are we adding a null object to the pool?"); - - // VSTFDEVDIV 742887 - When another thread is clearing this pool, it - // will set _cannotBePooled for all connections in this pool without prejudice which - // causes the following assert to fire, which really mucks up stress - // against checked bits. + Debug.Assert(obj != null, "why are we adding a null object to the pool?"); // Debug.Assert(obj.CanBePooled, "non-poolable object in pool"); SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Connection {1}, Pushing to general pool.", ObjectID, obj.ObjectID); @@ -1817,7 +1702,7 @@ internal void PutNewObject(DbConnectionInternal obj) internal void PutObject(DbConnectionInternal obj, object owningObject) { - Debug.Assert(null != obj, "null obj?"); + Debug.Assert(obj != null, "null obj"); PerformanceCounters.SoftDisconnectsPerSecond.Increment(); @@ -1836,8 +1721,6 @@ internal void PutObject(DbConnectionInternal obj, object owningObject) // means, is that we're now responsible for this connection: // it won't get reclaimed if it gets lost. obj.PrePush(owningObject); - - // TODO: Consider using a Cer to ensure that we mark the object for reclaimation in the event something bad happens? } DeactivateObject(obj); @@ -1845,7 +1728,7 @@ internal void PutObject(DbConnectionInternal obj, object owningObject) internal void PutObjectFromTransactedPool(DbConnectionInternal obj) { - Debug.Assert(null != obj, "null pooledObject?"); + Debug.Assert(obj != null, "null pooledObject?"); Debug.Assert(obj.EnlistedTransaction == null, "pooledObject is still enlisted?"); // called by the transacted connection pool , once it's removed the @@ -1871,7 +1754,7 @@ internal void PutObjectFromTransactedPool(DbConnectionInternal obj) private void QueuePoolCreateRequest() { - if (State.Running == _state) + if (_state == State.Running) { // Make sure we're at quota by posting a callback to the threadpool. ThreadPool.QueueUserWorkItem(_poolCreateRequest); @@ -1893,7 +1776,7 @@ private bool ReclaimEmancipatedObjects() { DbConnectionInternal obj = _objectList[i]; - if (null != obj) + if (obj != null) { bool locked = false; @@ -1948,7 +1831,6 @@ internal void Startup() { SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, CleanupWait={1}", ObjectID, _cleanupWait); _cleanupTimer = CreateCleanupTimer(); - if (NeedToReplenish) { QueuePoolCreateRequest(); @@ -1961,24 +1843,21 @@ internal void Shutdown() _state = State.ShuttingDown; // deactivate timer callbacks - Timer t = _cleanupTimer; + Timer timer = _cleanupTimer; _cleanupTimer = null; - if (null != t) - { - t.Dispose(); - } + timer?.Dispose(); } // TransactionEnded merely provides the plumbing for DbConnectionInternal to access the transacted pool // that is implemented inside DbConnectionPool. This method's counterpart (PutTransactedObject) should // only be called from DbConnectionPool.DeactivateObject and thus the plumbing to provide access to // other objects is unnecessary (hence the asymmetry of Ended but no Begin) - internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) + internal void TransactionEnded(Transaction transaction, DbConnectionInternal transactedObject) { - Debug.Assert(null != transaction, "null transaction?"); - Debug.Assert(null != transactedObject, "null transactedObject?"); + Debug.Assert(transaction != null, "null transaction?"); + Debug.Assert(transactedObject != null, "null transactedObject?"); - // Note: connection may still be associated with transaction due to Explicit Unbinding requirement. + // Note: connection may still be associated with transaction due to Explicit Unbinding requirement. SqlClientEventSource.Log.TryPoolerTraceEvent(" {0}, Transaction {1}, Connection {2}, Transaction Completed", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID); // called by the internal connection when it get's told that the @@ -1986,7 +1865,7 @@ internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionIntern // the connection from it's list, then we put the connection back in // general circulation. TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool; - if (null != transactedConnectionPool) + if (transactedConnectionPool != null) { transactedConnectionPool.TransactionEnded(transaction, transactedObject); } @@ -2004,14 +1883,14 @@ private DbConnectionInternal UserCreateRequest(DbConnection owningObject, DbConn } else { - if ((oldConnection != null) || (Count < MaxPoolSize) || (0 == MaxPoolSize)) + if ((oldConnection != null) || (Count < MaxPoolSize) || (MaxPoolSize == 0)) { // If we have an odd number of total objects, reclaim any dead objects. // If we did not find any objects to reclaim, create a new one. - - // TODO: Consider implement a control knob here; why do we only check for dead objects ever other time? why not every 10th time or every time? if ((oldConnection != null) || (Count & 0x1) == 0x1 || !ReclaimEmancipatedObjects()) + { obj = CreateObject(owningObject, userOptions, oldConnection); + } } return obj; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs index 6aa0847667..6a87505f4c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolGroup.cs @@ -191,7 +191,7 @@ internal DbConnectionPool GetConnectionPool(DbConnectionFactory connectionFactor { Debug.Assert(ADP.IsWindowsNT, "should not be pooling on Win9x"); - DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.NoIdentity; + DbConnectionPoolIdentity currentIdentity = DbConnectionPoolIdentity.s_noIdentity; if (_poolGroupOptions.PoolByIdentity) { // if we're pooling by identity (because integrated security is diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs deleted file mode 100644 index 341569145d..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs +++ /dev/null @@ -1,240 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.ProviderBase -{ - - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Runtime.Versioning; - using System.Security.Permissions; - using System.Security.Principal; - using Microsoft.Data.Common; - - [Serializable] // Serializable so SqlDependencyProcessDispatcher can marshall cross domain to SqlDependency. - sealed internal class DbConnectionPoolIdentity - { - private const int E_NotImpersonationToken = unchecked((int)0x8007051D); - private const int Win32_CheckTokenMembership = 1; - private const int Win32_GetTokenInformation_1 = 2; - private const int Win32_GetTokenInformation_2 = 3; - private const int Win32_ConvertSidToStringSidW = 4; - private const int Win32_CreateWellKnownSid = 5; - - static public readonly DbConnectionPoolIdentity NoIdentity = new DbConnectionPoolIdentity(String.Empty, false, true); - static private readonly byte[] NetworkSid = (ADP.IsWindowsNT ? CreateWellKnownSid(WellKnownSidType.NetworkSid) : null); - static private DbConnectionPoolIdentity _lastIdentity = null; - - private readonly string _sidString; - private readonly bool _isRestricted; - private readonly bool _isNetwork; - private readonly int _hashCode; - - private DbConnectionPoolIdentity(string sidString, bool isRestricted, bool isNetwork) - { - _sidString = sidString; - _isRestricted = isRestricted; - _isNetwork = isNetwork; - _hashCode = sidString == null ? 0 : sidString.GetHashCode(); - } - - internal bool IsRestricted - { - get { return _isRestricted; } - } - - internal bool IsNetwork - { - get { return _isNetwork; } - } - - static private byte[] CreateWellKnownSid(WellKnownSidType sidType) - { - // Passing an array as big as it can ever be is a small price to pay for - // not having to P/Invoke twice (once to get the buffer, once to get the data) - - uint length = (uint)SecurityIdentifier.MaxBinaryLength; - byte[] resultSid = new byte[length]; - - // NOTE - We copied this code from System.Security.Principal.Win32.CreateWellKnownSid... - - if (0 == UnsafeNativeMethods.CreateWellKnownSid((int)sidType, null, resultSid, ref length)) - { - IntegratedSecurityError(Win32_CreateWellKnownSid); - } - return resultSid; - } - - override public bool Equals(object value) - { - bool result = ((this == NoIdentity) || (this == value)); - if (!result && (null != value)) - { - DbConnectionPoolIdentity that = ((DbConnectionPoolIdentity)value); - result = ((this._sidString == that._sidString) && (this._isRestricted == that._isRestricted) && (this._isNetwork == that._isNetwork)); - } - return result; - } - - [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)] - static internal WindowsIdentity GetCurrentWindowsIdentity() - { - return WindowsIdentity.GetCurrent(); - } - - [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] - static private IntPtr GetWindowsIdentityToken(WindowsIdentity identity) - { - return identity.Token; - } - - [ResourceExposure(ResourceScope.None)] // SxS: this method does not create named objects - [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] - static internal DbConnectionPoolIdentity GetCurrent() - { - - // DEVNOTE: GetTokenInfo and EqualSID do not work on 9x. WindowsIdentity does not - // work either on 9x. In fact, after checking with native there is no way - // to validate the user on 9x, so simply don't. It is a known issue in - // native, and we will handle this the same way. - - if (!ADP.IsWindowsNT) - { - return NoIdentity; - } - - WindowsIdentity identity = GetCurrentWindowsIdentity(); - IntPtr token = GetWindowsIdentityToken(identity); // Free'd by WindowsIdentity. - uint bufferLength = 2048; // Suggested default given by Greg Fee. - uint lengthNeeded = 0; - - IntPtr tokenStruct = IntPtr.Zero; - IntPtr SID; - IntPtr sidStringBuffer = IntPtr.Zero; - bool isNetwork; - - // Win32NativeMethods.IsTokenRestricted will raise exception if the native call fails - bool isRestricted = Win32NativeMethods.IsTokenRestrictedWrapper(token); - - DbConnectionPoolIdentity current = null; - - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - if (!UnsafeNativeMethods.CheckTokenMembership(token, NetworkSid, out isNetwork)) - { - // will always fail with 0x8007051D if token is not an impersonation token - IntegratedSecurityError(Win32_CheckTokenMembership); - } - - RuntimeHelpers.PrepareConstrainedRegions(); - try - { } - finally - { - // allocating memory and assigning to tokenStruct must happen - tokenStruct = SafeNativeMethods.LocalAlloc(DbBuffer.LMEM_FIXED, (IntPtr)bufferLength); - } - if (IntPtr.Zero == tokenStruct) - { - throw new OutOfMemoryException(); - } - if (!UnsafeNativeMethods.GetTokenInformation(token, 1, tokenStruct, bufferLength, ref lengthNeeded)) - { - if (lengthNeeded > bufferLength) - { - bufferLength = lengthNeeded; - - RuntimeHelpers.PrepareConstrainedRegions(); - try - { } - finally - { - // freeing token struct and setting tokenstruct to null must happen together - // allocating memory and assigning to tokenStruct must happen - SafeNativeMethods.LocalFree(tokenStruct); - tokenStruct = IntPtr.Zero; // protect against LocalAlloc throwing an exception - tokenStruct = SafeNativeMethods.LocalAlloc(DbBuffer.LMEM_FIXED, (IntPtr)bufferLength); - } - if (IntPtr.Zero == tokenStruct) - { - throw new OutOfMemoryException(); - } - - if (!UnsafeNativeMethods.GetTokenInformation(token, 1, tokenStruct, bufferLength, ref lengthNeeded)) - { - IntegratedSecurityError(Win32_GetTokenInformation_1); - } - } - else - { - IntegratedSecurityError(Win32_GetTokenInformation_2); - } - } - - identity.Dispose(); // Keep identity variable alive until after GetTokenInformation calls. - - - SID = Marshal.ReadIntPtr(tokenStruct, 0); - - if (!UnsafeNativeMethods.ConvertSidToStringSidW(SID, out sidStringBuffer)) - { - IntegratedSecurityError(Win32_ConvertSidToStringSidW); - } - - if (IntPtr.Zero == sidStringBuffer) - { - throw ADP.InternalError(ADP.InternalErrorCode.ConvertSidToStringSidWReturnedNull); - } - - string sidString = Marshal.PtrToStringUni(sidStringBuffer); - - var lastIdentity = _lastIdentity; - if ((lastIdentity != null) && (lastIdentity._sidString == sidString) && (lastIdentity._isRestricted == isRestricted) && (lastIdentity._isNetwork == isNetwork)) - { - current = lastIdentity; - } - else - { - current = new DbConnectionPoolIdentity(sidString, isRestricted, isNetwork); - } - } - finally - { - // Marshal.FreeHGlobal does not have a ReliabilityContract - if (IntPtr.Zero != tokenStruct) - { - SafeNativeMethods.LocalFree(tokenStruct); - tokenStruct = IntPtr.Zero; - } - if (IntPtr.Zero != sidStringBuffer) - { - SafeNativeMethods.LocalFree(sidStringBuffer); - sidStringBuffer = IntPtr.Zero; - } - } - _lastIdentity = current; - return current; - } - - override public int GetHashCode() - { - return _hashCode; - } - - static private void IntegratedSecurityError(int caller) - { - // passing 1,2,3,4,5 instead of true/false so that with a debugger - // we could determine more easily which Win32 method call failed - int lastError = Marshal.GetHRForLastWin32Error(); - if ((Win32_CheckTokenMembership != caller) || (E_NotImpersonationToken != lastError)) - { - Marshal.ThrowExceptionForHR(lastError); // will only throw if (hresult < 0) - } - } - - } -} - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs index 919f8f2c4d..3f2f35e83d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -2067,7 +2067,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec if (_impersonateIdentity != null) { - using (WindowsIdentity identity = DbConnectionPoolIdentity.GetCurrentWindowsIdentity()) + using (WindowsIdentity identity = WindowsIdentity.GetCurrent()) { if (_impersonateIdentity.User == identity.User) { @@ -2086,7 +2086,7 @@ private bool TryOpen(TaskCompletionSource retry, SqlConnec { if (this.UsesIntegratedSecurity(connectionOptions) || this.UsesCertificate(connectionOptions) || this.UsesActiveDirectoryIntegrated(connectionOptions)) { - _lastIdentity = DbConnectionPoolIdentity.GetCurrentWindowsIdentity(); + _lastIdentity = WindowsIdentity.GetCurrent(); } else { diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs index 23dba04f58..20ca518552 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDependencyListener.cs @@ -131,7 +131,7 @@ internal SqlConnectionContainer(SqlConnectionContainerHashHelper hashHelper, str // For now, DbConnectionPoolIdentity does not cache WindowsIdentity. // That means for every container creation, we create a WindowsIdentity twice. // We may want to improve this. - _windowsIdentity = DbConnectionPoolIdentity.GetCurrentWindowsIdentity(); + _windowsIdentity = WindowsIdentity.GetCurrent(); } _escapedQueueName = SqlConnection.FixupDatabaseTransactionName(_queue); // Properly escape to prevent SQL Injection. diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.cs new file mode 100644 index 0000000000..16c7c58950 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObjectFactory.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Data.SqlClient +{ + internal sealed class TdsParserStateObjectFactory + { + public static bool UseManagedSNI => true; + } +} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Unix.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Unix.cs similarity index 58% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Unix.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Unix.cs index 95769c3e6c..3e6c4450fa 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Unix.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Unix.cs @@ -2,10 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Runtime.Versioning; + namespace Microsoft.Data.ProviderBase { - partial class DbConnectionPoolIdentity + internal sealed partial class DbConnectionPoolIdentity { + [ResourceExposure(ResourceScope.None)] // SxS: this method does not create named objects + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] internal static DbConnectionPoolIdentity GetCurrent() { return GetCurrentManaged(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Windows.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Windows.cs similarity index 84% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Windows.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Windows.cs index 93d2b0f329..3b2f45baab 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Windows.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.Windows.cs @@ -3,15 +3,18 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.Versioning; using System.Security.Principal; using Microsoft.Data.SqlClient; namespace Microsoft.Data.ProviderBase { - partial class DbConnectionPoolIdentity + internal sealed partial class DbConnectionPoolIdentity { - private static DbConnectionPoolIdentity s_lastIdentity = null; + private static DbConnectionPoolIdentity s_lastIdentity; + [ResourceExposure(ResourceScope.None)] // SxS: this method does not create named objects + [ResourceConsumption(ResourceScope.Process, ResourceScope.Process)] internal static DbConnectionPoolIdentity GetCurrent() { return TdsParserStateObjectFactory.UseManagedSNI ? GetCurrentManaged() : GetCurrentNative(); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs similarity index 57% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs index f1b985a6de..99c1335325 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbConnectionPoolIdentity.cs @@ -2,11 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; + namespace Microsoft.Data.ProviderBase { - sealed internal partial class DbConnectionPoolIdentity + [Serializable] // Serializable so SqlDependencyProcessDispatcher can marshall cross domain to SqlDependency. + internal sealed partial class DbConnectionPoolIdentity { - public static readonly DbConnectionPoolIdentity NoIdentity = new DbConnectionPoolIdentity(string.Empty, false, true); + public static readonly DbConnectionPoolIdentity s_noIdentity = new DbConnectionPoolIdentity(string.Empty, false, true); private readonly string _sidString; private readonly bool _isRestricted; @@ -18,22 +21,21 @@ private DbConnectionPoolIdentity(string sidString, bool isRestricted, bool isNet _sidString = sidString; _isRestricted = isRestricted; _isNetwork = isNetwork; - _hashCode = sidString == null ? 0 : sidString.GetHashCode(); - } - - internal bool IsRestricted - { - get { return _isRestricted; } + _hashCode = sidString?.GetHashCode() ?? 0; } + internal bool IsRestricted => _isRestricted; - override public bool Equals(object value) + public override bool Equals(object value) { - bool result = ((this == NoIdentity) || (this == value)); - if (!result && (null != value)) + bool result = ReferenceEquals(this, s_noIdentity) || ReferenceEquals(this, value); + if (!result && (!(value is null))) { - DbConnectionPoolIdentity that = ((DbConnectionPoolIdentity)value); - result = ((_sidString == that._sidString) && (_isRestricted == that._isRestricted) && (_isNetwork == that._isNetwork)); + DbConnectionPoolIdentity that = (DbConnectionPoolIdentity)value; + result = + string.Equals(_sidString,that._sidString,System.StringComparison.Ordinal) && + (_isRestricted == that._isRestricted) && + (_isNetwork == that._isNetwork); } return result; }