Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Data / System / Data / ProviderBase / DbConnectionPool.cs / 3 / DbConnectionPool.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// [....]
//-----------------------------------------------------------------------------
namespace System.Data.ProviderBase {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Threading;
using SysTx = System.Transactions;
sealed internal class DbConnectionPool {
private enum State {
Initializing,
Running,
ShuttingDown,
}
internal const Bid.ApiGroup PoolerTracePoints = (Bid.ApiGroup)0x1000;
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// [PS SQL Hotfix RFC # 50003517: Current Transaction is disposed within the transaction scope,
// before leaving TransactionScope block against SQL Server 2008]
// 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 SysTx.Transaction _transaction;
internal TransactedConnectionList(int initialAllocation, SysTx.Transaction tx) : base(initialAllocation) {
_transaction = tx;
}
internal void Dispose() {
if (null != _transaction) {
_transaction.Dispose();
}
}
}
sealed private class TransactedConnectionPool : Hashtable {
DbConnectionPool _pool;
private static int _objectTypeCount; // Bid counter
internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
internal TransactedConnectionPool(DbConnectionPool pool) : base() {
Debug.Assert(null != pool, "null pool?");
_pool = pool;
Bid.PoolerTrace(" %d#, Constructed for connection pool %d#\n", ObjectID, _pool.ObjectID);
}
internal int ObjectID {
get {
return _objectID;
}
}
internal DbConnectionPool Pool {
get {
return _pool;
}
}
internal DbConnectionInternal GetTransactedObject(SysTx.Transaction transaction) {
Debug.Assert(null != transaction, "null transaction?");
DbConnectionInternal transactedObject = null;
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// Back porting a partial fix for Transaction Dispose issue observed in Dev10.
// Rest of the fix including the synchronization issue will be addressed the any subsequent release.
if (null != connections) {
lock (connections) {
int i = connections.Count - 1;
if (0 <= i) {
transactedObject = connections[i];
connections.RemoveAt(i);
}
}
}
if (null != transactedObject) {
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Popped.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
}
return transactedObject;
}
internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
Debug.Assert(null != transaction, "null transaction?");
Debug.Assert(null != transactedObject, "null transactedObject?");
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Pushing.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
// NOTE: it is possible that the connecton was put on the
// deactivate queue, and while it was on the queue, the
// transaction ended, causing the list for the transaction
// to have been removed. In that case, we can't expect
// the list to be here.
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// Back porting a partial fix for Transaction Dispose issue observed in Dev10.
// Rest of the fix including the synchronization issue will be addressed the any subsequent release.
if (null != connections) {
lock (connections) {
Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?");
connections.Add(transactedObject);
Pool.PerformanceCounters.NumberOfFreeConnections.Increment();
}
}
}
internal void TransactionBegin(SysTx.Transaction transaction) {
Bid.PoolerTrace(" %d#, Transaction %d#, Begin.\n", ObjectID, transaction.GetHashCode());
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
if (null == connections) {
SysTx.Transaction transactionClone = null;
try {
transactionClone = transaction.Clone();
TransactedConnectionList newConnections = new TransactedConnectionList(2, transactionClone); // start with only two connections in the list; most times we won't need that many.
Bid.PoolerTrace(" %d#, Transaction %d#, Adding List to transacted pool.\n", ObjectID, transaction.GetHashCode());
lock (this) {
connections = (TransactedConnectionList)this[transactionClone];
if (null == connections) {
connections = newConnections;
this.Add(transactionClone, connections);
transactionClone = null; // we've used it -- don't throw it away.
}
}
}
finally {
if (null != transactionClone) {
transactionClone.Dispose();
}
}
Bid.PoolerTrace(" %d#, Transaction %d#, Added.\n", ObjectID, transaction.GetHashCode());
}
}
internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
int entry = -1;
// NOTE: we may be ending a transaction for a connection that is
// currently not in the pool, and therefore it may not have
// a list for it, because it may have been removed already.
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// Back porting a partial fix for Transaction Dispose issue observed in Dev10.
// Rest of the fix including the synchronization issue will be addressed the any subsequent release.
if (null != connections) {
lock (connections) {
entry = connections.IndexOf(transactedObject);
if (entry >= 0) {
connections.RemoveAt(entry);
}
// Once we've completed all the ended notifications, we can
// safely remove the list from the transacted pool.
if (0 >= connections.Count) {
Bid.PoolerTrace(" %d#, Transaction %d#, Removing List from transacted pool.\n", ObjectID, transaction.GetHashCode());
lock (this) {
Remove(transaction);
}
Bid.PoolerTrace(" %d#, Transaction %d#, Removed.\n", ObjectID, transaction.GetHashCode());
// we really need to dispose our connection list; it may have
// native resources via the tx and GC may not happen soon enough.
connections.Dispose();
}
}
}
// If (and only if) we found the connection in the list of
// connections, we'll put it back...
if (0 <= entry) {
Pool.PerformanceCounters.NumberOfFreeConnections.Decrement();
Pool.PutObjectFromTransactedPool(transactedObject);
}
}
}
private sealed class PoolWaitHandles : DbBuffer {
private readonly Semaphore _poolSemaphore;
private readonly ManualResetEvent _errorEvent;
// Using a Mutex requires ThreadAffinity because SQL CLR can swap
// the underlying Win32 thread associated with a managed thread in preemptive mode.
// Using an AutoResetEvent does not have that complication.
private readonly Semaphore _creationSemaphore;
private readonly SafeHandle _poolHandle;
private readonly SafeHandle _errorHandle;
private readonly SafeHandle _creationHandle;
private readonly int _releaseFlags;
internal PoolWaitHandles(Semaphore poolSemaphore, ManualResetEvent errorEvent, Semaphore creationSemaphore) : base(3*IntPtr.Size) {
bool mustRelease1 = false, mustRelease2 = false, mustRelease3 = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
_poolSemaphore = poolSemaphore;
_errorEvent = errorEvent;
_creationSemaphore = creationSemaphore;
// because SafeWaitHandle doesn't have reliability contract
_poolHandle = poolSemaphore.SafeWaitHandle;
_errorHandle = errorEvent.SafeWaitHandle;
_creationHandle = creationSemaphore.SafeWaitHandle;
_poolHandle.DangerousAddRef(ref mustRelease1);
_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");
WriteIntPtr(SEMAPHORE_HANDLE*IntPtr.Size, _poolHandle.DangerousGetHandle());
WriteIntPtr(ERROR_HANDLE*IntPtr.Size, _errorHandle.DangerousGetHandle());
WriteIntPtr(CREATION_HANDLE*IntPtr.Size, _creationHandle.DangerousGetHandle());
}
finally {
if (mustRelease1) {
_releaseFlags |= 1;
}
if (mustRelease2) {
_releaseFlags |= 2;
}
if (mustRelease3) {
_releaseFlags |= 4;
}
}
}
internal SafeHandle CreationHandle {
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
get { return _creationHandle; }
}
internal Semaphore CreationSemaphore {
get { return _creationSemaphore; }
}
internal ManualResetEvent ErrorEvent {
get { return _errorEvent; }
}
internal Semaphore PoolSemaphore {
get { return _poolSemaphore; }
}
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)) {
_poolHandle.DangerousRelease();
}
if (0 != (2 & _releaseFlags)) {
_errorHandle.DangerousRelease();
}
if (0 != (4 & _releaseFlags)) {
_creationHandle.DangerousRelease();
}
return base.ReleaseHandle();
}
}
private const int MAX_Q_SIZE = (int)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 WAIT_OBJECT_0 = 0;
private const int WAIT_TIMEOUT = (int)0x102;
private const int WAIT_ABANDONED = (int)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 readonly int _cleanupWait;
private readonly DbConnectionPoolIdentity _identity;
private readonly DbConnectionFactory _connectionFactory;
private readonly DbConnectionPoolGroup _connectionPoolGroup;
private readonly DbConnectionPoolGroupOptions _connectionPoolGroupOptions;
private DbConnectionPoolProviderInfo _connectionPoolProviderInfo;
private State _state;
private readonly DbConnectionInternalListStack _stackOld = new DbConnectionInternalListStack();
private readonly DbConnectionInternalListStack _stackNew = new DbConnectionInternalListStack();
private readonly WaitCallback _poolCreateRequest;
private readonly Queue _deactivateQueue;
private readonly WaitCallback _deactivateCallback;
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; // Bid counter
internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
// only created by DbConnectionPoolGroup.GetConnectionPool
internal DbConnectionPool(
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");
if ((null != identity) && identity.IsRestricted) {
throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToPoolOnRestrictedToken);
}
_state= State.Initializing;
lock(_random) { // Random.Next is not thread-safe
_cleanupWait = _random.Next(12, 24)*10*1000; // 2-4 minutes in 10 sec intervals, WebData 103603
}
_connectionFactory = connectionFactory;
_connectionPoolGroup = connectionPoolGroup;
_connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions;
_connectionPoolProviderInfo = connectionPoolProviderInfo;
_identity = identity;
if (UseDeactivateQueue) {
_deactivateQueue = new Queue();
_deactivateCallback = new WaitCallback(ProcessDeactivateQueue);
}
_waitHandles = new PoolWaitHandles(
new Semaphore(0, MAX_Q_SIZE),
new ManualResetEvent(false),
new Semaphore(1, 1));
_errorWait = ERROR_WAIT_DEFAULT;
_errorTimer = null; // No error yet.
_objectList = new List(MaxPoolSize);
if(ADP.IsPlatformNT5) {
_transactedConnectionPool = new TransactedConnectionPool(this);
}
_poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback
_state = State.Running;
Bid.PoolerTrace(" %d#, Constructed.\n", ObjectID);
//_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls
// StartBackgroundCallbacks after pool is actually in the collection
}
private int CreationTimeout {
get { return PoolGroupOptions.CreationTimeout; }
}
internal int Count {
get { return _totalObjects; }
}
internal DbConnectionFactory ConnectionFactory {
get { return _connectionFactory; }
}
internal bool ErrorOccurred {
get { return _errorOccurred; }
}
private bool HasTransactionAffinity {
get { return PoolGroupOptions.HasTransactionAffinity; }
}
internal TimeSpan LoadBalanceTimeout {
get { return PoolGroupOptions.LoadBalanceTimeout; }
}
private bool NeedToReplenish {
get {
if (State.Running != _state) // SQL BU DT 364595 - 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 waitingRequests = _waitCount;
bool needToReplenish = (freeObjects < waitingRequests) || ((freeObjects == waitingRequests) && (totalObjects > 1));
return needToReplenish;
}
}
internal DbConnectionPoolIdentity Identity {
get { return _identity; }
}
internal bool IsRunning {
get { return State.Running == _state; }
}
private int MaxPoolSize {
get { return PoolGroupOptions.MaxPoolSize; }
}
private int MinPoolSize {
get { return PoolGroupOptions.MinPoolSize; }
}
internal int ObjectID {
get {
return _objectID;
}
}
internal DbConnectionPoolCounters PerformanceCounters {
get { return _connectionFactory.PerformanceCounters; }
}
internal DbConnectionPoolGroup PoolGroup {
get { return _connectionPoolGroup; }
}
internal DbConnectionPoolGroupOptions PoolGroupOptions {
get { return _connectionPoolGroupOptions; }
}
internal DbConnectionPoolProviderInfo ProviderInfo {
get { return _connectionPoolProviderInfo; }
}
private bool UseDeactivateQueue {
get { return PoolGroupOptions.UseDeactivateQueue; }
}
internal bool UseLoadBalancing {
get { return PoolGroupOptions.UseLoadBalancing; }
}
private bool UsingIntegrateSecurity {
get { return (null != _identity && DbConnectionPoolIdentity.NoIdentity != _identity); }
}
private void CleanupCallback(Object state) {
// Called when the cleanup-timer ticks over.
// This is the automatic prunning method. Every period, we will
// perform a two-step process:
//
// First, for each free object above MinPoolSize, we will obtain a
// semaphore representing one object and destroy one from old stack.
// We will continue this until we either reach MinPoolSize, we are
// unable to obtain a free object, or we have exhausted all the
// objects on the old stack.
//
// Second we move all free objects on the new stack to the old stack.
// So, every period the objects on the old stack are destroyed and
// the objects on the new stack are pushed to the old stack. All
// objects that are currently out and in use are not on either stack.
//
// With this logic, objects are pruned from the pool if unused for
// at least one period but not more than two periods.
Bid.PoolerTrace(" %d#\n", ObjectID);
// Destroy free objects that put us above MinPoolSize from old stack.
while(Count > MinPoolSize) { // While above MinPoolSize...
if (_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
// We obtained a objects from the semaphore.
DbConnectionInternal obj = _stackOld.SynchronizedPop();
if (null != obj) {
// If we obtained one from the old stack, destroy it.
PerformanceCounters.NumberOfFreeConnections.Decrement();
// Transaction roots must survive even aging out (TxEnd event will clean them up).
bool shouldDestroy = true;
lock (obj) { // Lock to prevent ---- window between IsTransactionRoot and shouldDestroy assignment
if (obj.IsTransactionRoot) {
shouldDestroy = false;
}
}
// !!!!!!!!!! WARNING !!!!!!!!!!!!!
// ONLY touch obj after lock release if shouldDestroy is false!!! Otherwise, it may be destroyed
// by transaction-end thread!
// Note that there is a minor race condition between this task and the transaction end event, if the latter runs
// between the lock above and the SetInStasis call below. The reslult is that the stasis counter may be
// incremented without a corresponding decrement (the transaction end task is normally expected
// to decrement, but will only do so if the stasis flag is set when it runs). I've minimized the size
// of the window, but we aren't totally eliminating it due to SetInStasis needing to do bid tracing, which
// we don't want to do under this lock, if possible. It should be possible to eliminate this ---- with
// more substantial re-architecture of the pool, but we don't have the time to do that work for the current release.
if (shouldDestroy) {
DestroyObject(obj);
}
else {
obj.SetInStasis();
}
}
else {
// Else we exhausted the old stack (the object the
// semaphore represents is on the new stack), so break.
_waitHandles.PoolSemaphore.Release(1);
break;
}
}
else {
break;
}
}
// Push to the old-stack. For each free object, move object from
// new stack to old stack.
if(_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
for(;;) {
DbConnectionInternal obj = _stackNew.SynchronizedPop();
if (null == obj)
break;
Bid.PoolerTrace(" %d#, ChangeStacks=%d#\n", ObjectID, obj.ObjectID);
Debug.Assert(!obj.IsEmancipated, "pooled object not in pool");
Debug.Assert(obj.CanBePooled, "pooled object is not poolable");
_stackOld.SynchronizedPush(obj);
}
_waitHandles.PoolSemaphore.Release(1);
}
// Queue up a request to bring us up to MinPoolSize
QueuePoolCreateRequest();
}
internal void Clear() {
Bid.PoolerTrace(" %d#, Clearing.\n", ObjectID);
DbConnectionInternal obj;
// First, quickly doom everything.
lock(_objectList) {
int count = _objectList.Count;
for (int i = 0; i < count; ++i) {
obj = _objectList[i];
if (null != obj) {
obj.DoNotPoolThisConnection();
}
}
}
// Second, dispose of all the free connections.
while (null != (obj = _stackNew.SynchronizedPop())) {
PerformanceCounters.NumberOfFreeConnections.Decrement();
DestroyObject(obj);
}
while (null != (obj = _stackOld.SynchronizedPop())) {
PerformanceCounters.NumberOfFreeConnections.Decrement();
DestroyObject(obj);
}
// Finally, reclaim everything that's emancipated (which, because
// it's been doomed, will cause it to be disposed of as well)
ReclaimEmancipatedObjects();
Bid.PoolerTrace(" %d#, Cleared.\n", ObjectID);
}
private Timer CreateCleanupTimer() {
return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait));
}
private DbConnectionInternal CreateObject(DbConnection owningObject) {
DbConnectionInternal newObj = null;
try {
newObj = _connectionFactory.CreatePooledConnection(owningObject, this, _connectionPoolGroup.ConnectionOptions);
if (null == newObj) {
throw ADP.InternalError(ADP.InternalErrorCode.CreateObjectReturnedNull); // CreateObject succeeded, but null object
}
if (!newObj.CanBePooled) {
throw ADP.InternalError(ADP.InternalErrorCode.NewObjectCannotBePooled); // CreateObject succeeded, but non-poolable object
}
newObj.PrePush(null);
lock (_objectList) {
_objectList.Add(newObj);
_totalObjects = _objectList.Count;
PerformanceCounters.NumberOfPooledConnections.Increment(); //
}
Bid.PoolerTrace(" %d#, Connection %d#, Added to pool.\n", ObjectID, newObj.ObjectID);
// Reset the error wait:
_errorWait = ERROR_WAIT_DEFAULT;
}
catch(Exception e) {
//
if (!ADP.IsCatchableExceptionType(e)) {
throw;
}
ADP.TraceExceptionForCapture(e);
newObj = null; // set to null, so we do not return bad new object
// Failed to create instance
_resError = e;
_waitHandles.ErrorEvent.Set();
_errorOccurred = true;
_errorTimer = new Timer(new TimerCallback(this.ErrorCallback), null, _errorWait, _errorWait);
if (30000 < _errorWait) {
_errorWait = 60000;
}
else {
_errorWait *= 2;
}
throw;
}
return newObj;
}
private void DeactivateObject(DbConnectionInternal obj) {
Bid.PoolerTrace(" %d#, Connection %d#, Deactivating.\n", ObjectID, obj.ObjectID);
obj.DeactivateConnection(); // we presume this operation is safe outside of a lock...
if ((State.Running == _state) && !obj.IsConnectionDoomed) {
bool returnToGeneralPool = true;
lock (obj) {
// A connection with a delegated transaction cannot currently
// be returned to a different customer until the transaction
// actually completes, so we send it into Stasis -- the SysTx
// transaction object will ensure that it is owned (not lost),
// and it will be certain to put it back into the pool.
if (obj.IsNonPoolableTransactionRoot) {
obj.SetInStasis();
returnToGeneralPool = false;
}
else {
// We must put this connection into the transacted pool
// while inside a lock to prevent a race condition with
// the transaction asyncronously completing on a second
// thread.
SysTx.Transaction transaction = obj.EnlistedTransaction;
if (null != transaction) {
TransactionBegin(transaction); // Delayed creation of transacted pool
_transactedConnectionPool.PutTransactedObject(transaction, obj);
returnToGeneralPool = false;
}
}
}
// Only push the connection into the general pool if we didn't
// already push it onto the transacted pool.
if (returnToGeneralPool) {
PutNewObject(obj);
}
}
else {
// the object is not fit for reuse -- just dispose of it.
DestroyObject(obj);
QueuePoolCreateRequest();
}
}
private void DestroyObject(DbConnectionInternal obj) {
// A connection with a delegated transaction cannot be disposed of
// until the delegated transaction has actually completed. Instead,
// we simply leave it alone; when the transaction completes, it will
// come back through PutObjectFromTransactedPool, which will call us
// again.
if (obj.IsTxRootWaitingForTxEnd) {
Bid.PoolerTrace(" %d#, Connection %d#, Has Delegated Transaction, waiting to Dispose.\n", ObjectID, obj.ObjectID);
}
else {
Bid.PoolerTrace(" %d#, Connection %d#, Removing from pool.\n", ObjectID, obj.ObjectID);
bool removed = false;
lock (_objectList) {
removed = _objectList.Remove(obj);
Debug.Assert(removed, "attempt to DestroyObject not in list");
_totalObjects = _objectList.Count;
}
if (removed) {
Bid.PoolerTrace(" %d#, Connection %d#, Removed from pool.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfPooledConnections.Decrement();
}
obj.Dispose();
Bid.PoolerTrace(" %d#, Connection %d#, Disposed.\n", ObjectID, obj.ObjectID);
PerformanceCounters.HardDisconnectsPerSecond.Increment();
}
}
private void ErrorCallback(Object state) {
Bid.PoolerTrace(" %d#, Resetting Error handling.\n", ObjectID);
_errorOccurred = false;
_waitHandles.ErrorEvent.Reset();
Timer t = _errorTimer;
_errorTimer = null;
if (t != null) {
t.Dispose(); // Cancel timer request.
}
}
internal DbConnectionInternal GetConnection(DbConnection owningObject) {
DbConnectionInternal obj = null;
SysTx.Transaction transaction = null;
PerformanceCounters.SoftConnectsPerSecond.Increment();
if(_state != State.Running) {
Bid.PoolerTrace(" %d#, DbConnectionInternal State != Running.\n", ObjectID);
return null;
}
Bid.PoolerTrace(" %d#, Getting connection.\n", ObjectID);
// If automatic transaction enlistment is required, then we try to
// get the connection from the transacted connection pool first.
if (HasTransactionAffinity) {
obj = GetFromTransactedPool(out transaction);
}
if (null == obj) {
Interlocked.Increment(ref _waitCount);
uint waitHandleCount = 3;
uint timeout = (uint)CreationTimeout;
do {
int waitResult = BOGUS_HANDLE;
int releaseSemaphoreResult = 0;
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
_waitHandles.DangerousAddRef(ref mustRelease);
// We absolutely must have the value of waitResult set,
// or we may leak the mutex in async abort cases.
RuntimeHelpers.PrepareConstrainedRegions();
try {
Debug.Assert(2 == waitHandleCount || 3 == waitHandleCount, "unexpected waithandle count");
}
finally {
waitResult = SafeNativeMethods.WaitForMultipleObjectsEx(waitHandleCount, _waitHandles.DangerousGetHandle(), false, timeout, false);
}
// From the WaitAny docs: "If more than one object became signaled during
// the call, this is the array index of the signaled object with the
// smallest index value of all the signaled objects." This is important
// so that the free object signal will be returned before a creation
// signal.
switch (waitResult) {
case WAIT_TIMEOUT:
Bid.PoolerTrace(" %d#, Wait timed out.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
return null;
case ERROR_HANDLE:
// Throw the error that PoolCreateRequest stashed.
Bid.PoolerTrace(" %d#, Errors are set.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw _resError;
case CREATION_HANDLE:
Bid.PoolerTrace(" %d#, Creating new connection.\n", ObjectID);
try {
obj = UserCreateRequest(owningObject);
}
catch {
if (null == obj) {
Interlocked.Decrement(ref _waitCount);
}
throw;
}
finally {
// SQLBUDT #386664 - ensure that we release this waiter, regardless
// of any exceptions that may be thrown.
if (null != obj) {
Interlocked.Decrement(ref _waitCount);
}
}
if (null == obj) {
// 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.
//
if (Count >= MaxPoolSize && 0 != MaxPoolSize) {
if (!ReclaimEmancipatedObjects()) {
// modify handle array not to wait on creation mutex anymore
Debug.Assert(2 == CREATION_HANDLE, "creation handle changed value");
waitHandleCount = 2;
}
}
}
break;
case SEMAPHORE_HANDLE:
//
// guaranteed available inventory
//
Interlocked.Decrement(ref _waitCount);
obj = GetFromGeneralPool();
break;
case WAIT_FAILED:
Bid.PoolerTrace(" %d#, Wait failed.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
goto default; // if ThrowExceptionForHR didn't throw for some reason
case (WAIT_ABANDONED+SEMAPHORE_HANDLE):
Bid.PoolerTrace(" %d#, Semaphore handle abandonded.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw new AbandonedMutexException(SEMAPHORE_HANDLE,_waitHandles.PoolSemaphore);
case (WAIT_ABANDONED+ERROR_HANDLE):
Bid.PoolerTrace(" %d#, Error handle abandonded.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw new AbandonedMutexException(ERROR_HANDLE,_waitHandles.ErrorEvent);
case (WAIT_ABANDONED+CREATION_HANDLE):
Bid.PoolerTrace(" %d#, Creation handle abandoned.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw new AbandonedMutexException(CREATION_HANDLE,_waitHandles.CreationSemaphore);
default:
Bid.PoolerTrace(" %d#, WaitForMultipleObjects=%d\n", ObjectID, waitResult);
Interlocked.Decrement(ref _waitCount);
throw ADP.InternalError(ADP.InternalErrorCode.UnexpectedWaitAnyResult);
}
}
finally {
if (CREATION_HANDLE == waitResult) {
int result = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
if (0 == result) { // failure case
releaseSemaphoreResult = Marshal.GetHRForLastWin32Error();
}
}
if (mustRelease) {
_waitHandles.DangerousRelease();
}
}
if (0 != releaseSemaphoreResult) {
Marshal.ThrowExceptionForHR(releaseSemaphoreResult); // will only throw if (hresult < 0)
}
} while (null == obj);
}
if (null != obj) {
lock (obj) { // Protect against Clear and ReclaimEmancipatedObjects, which call IsEmancipated, which is affected by PrePush and PostPop
obj.PostPop(owningObject);
}
try {
obj.ActivateConnection(transaction);
}
catch(SecurityException) {
// if Activate throws an exception
// put it back in the pool or have it properly disposed of
this.PutObject(obj, owningObject);
throw;
}
}
return(obj);
}
private DbConnectionInternal GetFromGeneralPool() {
DbConnectionInternal obj = null;
obj = _stackNew.SynchronizedPop();
if (null == obj) {
obj = _stackOld.SynchronizedPop();
}
// SQLBUDT #356870 -- 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!");
if (null != obj) {
Bid.PoolerTrace(" %d#, Connection %d#, Popped from general pool.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfFreeConnections.Decrement();
}
return(obj);
}
private DbConnectionInternal GetFromTransactedPool(out SysTx.Transaction transaction) {
transaction = ADP.GetCurrentTransaction();
DbConnectionInternal obj = null;
if (null != transaction && null != _transactedConnectionPool) {
obj = _transactedConnectionPool.GetTransactedObject(transaction);
if (null != obj) {
Bid.PoolerTrace(" %d#, Connection %d#, Popped from transacted pool.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfFreeConnections.Decrement();
}
}
return obj;
}
private void PoolCreateRequest(object state) {
// called by pooler to ensure pool requests are currently being satisfied -
// creation mutex has not been obtained
IntPtr hscp;
Bid.PoolerScopeEnter(out hscp, " %d#\n", ObjectID);
try {
if (State.Running == _state) {
// Before creating any new objects, reclaim any released objects that were
// not closed.
ReclaimEmancipatedObjects();
if (!ErrorOccurred) {
if (NeedToReplenish) {
// Check to see if pool was created using integrated security and if so, make
// sure the identity of current user matches that of user that created pool.
// If it doesn't match, do not create any objects on the ThreadPool thread,
// since either Open will fail or we will open a object for this pool that does
// not belong in this pool. The side effect of this is that if using integrated
// security min pool size cannot be guaranteed.
if (UsingIntegrateSecurity && !_identity.Equals(DbConnectionPoolIdentity.GetCurrent())) {
return;
}
bool mustRelease = false;
int waitResult = BOGUS_HANDLE;
uint timeout = (uint)CreationTimeout;
RuntimeHelpers.PrepareConstrainedRegions();
try {
_waitHandles.DangerousAddRef(ref mustRelease);
// Obtain creation mutex so we're the only one creating objects
// and we must have the wait result
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
waitResult = SafeNativeMethods.WaitForSingleObjectEx(_waitHandles.CreationHandle.DangerousGetHandle(), timeout, false);
}
if (WAIT_OBJECT_0 == waitResult) {
DbConnectionInternal newObj;
// Check ErrorOccurred again after obtaining mutex
if (!ErrorOccurred) {
while (NeedToReplenish) {
newObj = CreateObject((DbConnection)null);
// We do not need to check error flag here, since we know if
// CreateObject returned null, we are in error case.
if (null != newObj) {
PutNewObject(newObj);
}
else {
break;
}
}
}
}
else if (WAIT_TIMEOUT == waitResult) {
// do not wait forever and potential block this worker thread
// instead wait for a period of time and just requeue to try again
QueuePoolCreateRequest();
}
else {
// trace waitResult and ignore the failure
Bid.PoolerTrace(" %d#, PoolCreateRequest called WaitForSingleObject failed %d", ObjectID, waitResult);
}
}
catch (Exception e) {
//
if (!ADP.IsCatchableExceptionType(e)) {
throw;
}
// Now that CreateObject can throw, we need to catch the exception and discard it.
// There is no further action we can take beyond tracing. The error will be
// thrown to the user the next time they request a connection.
Bid.PoolerTrace(" %d#, PoolCreateRequest called CreateConnection which threw an exception: " + e.ToString(), ObjectID);
}
finally {
if (WAIT_OBJECT_0 == waitResult) {
// reuse waitResult and ignore its value
waitResult = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
}
if (mustRelease) {
_waitHandles.DangerousRelease();
}
}
}
}
}
}
finally {
Bid.ScopeLeave(ref hscp);
}
}
private void ProcessDeactivateQueue(object state) {
IntPtr hscp;
Bid.PoolerScopeEnter(out hscp, " %d#\n", ObjectID);
try {
object[] deactivateQueue;
lock (_deactivateQueue.SyncRoot) {
deactivateQueue = _deactivateQueue.ToArray();
_deactivateQueue.Clear();
}
foreach (DbConnectionInternal obj in deactivateQueue) {
PerformanceCounters.NumberOfStasisConnections.Decrement();
DeactivateObject(obj);
}
}
finally {
Bid.ScopeLeave(ref hscp);
}
}
internal void PutNewObject(DbConnectionInternal obj) {
Debug.Assert(null != obj, "why are we adding a null object to the pool?");
Debug.Assert(obj.CanBePooled, "non-poolable object in pool");
Bid.PoolerTrace(" %d#, Connection %d#, Pushing to general pool.\n", ObjectID, obj.ObjectID);
_stackNew.SynchronizedPush(obj);
_waitHandles.PoolSemaphore.Release(1);
PerformanceCounters.NumberOfFreeConnections.Increment();
}
internal void PutObject(DbConnectionInternal obj, object owningObject) {
Debug.Assert(null != obj, "null obj?");
PerformanceCounters.SoftDisconnectsPerSecond.Increment();
// Once a connection is closing (which is the state that we're in at
// this point in time) you cannot delegate a transaction to or enlist
// a transaction in it, so we can correctly presume that if there was
// not a delegated or enlisted transaction to start with, that there
// will not be a delegated or enlisted transaction once we leave the
// lock.
lock (obj) {
// Calling PrePush prevents the object from being reclaimed
// once we leave the lock, because it sets _pooledCount such
// that it won't appear to be out of the pool. What that
// means, is that we're now responsible for this connection:
// it won't get reclaimed if we drop the ball somewhere.
obj.PrePush(owningObject);
//
}
if (UseDeactivateQueue) {
// If we're using the DeactivateQueue, we'll just queue it up and
// be done; all the hard work will be done on the despooler thread.
bool needToQueueWorkItem;
Bid.PoolerTrace(" %d#, Connection %d#, Queueing for deactivation.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfStasisConnections.Increment();
lock (_deactivateQueue.SyncRoot) {
needToQueueWorkItem = (0 == _deactivateQueue.Count);
_deactivateQueue.Enqueue(obj);
}
if (needToQueueWorkItem) {
// Make sure we actually get around to deactivating the object
// and making it available again.
ThreadPool.QueueUserWorkItem(_deactivateCallback, null);
}
}
else {
// no deactivate queue -- do the work right now.
DeactivateObject(obj);
}
}
internal void PutObjectFromTransactedPool(DbConnectionInternal obj) {
Debug.Assert(null != obj, "null pooledObject?");
Debug.Assert(!obj.HasEnlistedTransaction, "pooledObject is still enlisted?");
// called by the transacted connection pool , once it's removed the
// connection from it's list. We put the connection back in general
// circulation.
// NOTE: there is no locking required here because if we're in this
// method, we can safely presume that the caller is the only person
// that is using the connection, and that all pre-push logic has been
// done and all transactions are ended.
Bid.PoolerTrace(" %d#, Connection %d#, Transaction has ended.\n", ObjectID, obj.ObjectID);
if (_state == State.Running && obj.CanBePooled) {
PutNewObject(obj);
}
else {
DestroyObject(obj);
QueuePoolCreateRequest();
}
}
private void QueuePoolCreateRequest() {
if (State.Running == _state) {
// Make sure we're at quota by posting a callback to the threadpool.
ThreadPool.QueueUserWorkItem(_poolCreateRequest);
}
}
private bool ReclaimEmancipatedObjects() {
bool emancipatedObjectFound = false;
Bid.PoolerTrace(" %d#\n", ObjectID);
List reclaimedObjects = new List();
int count;
lock(_objectList) {
count = _objectList.Count;
for (int i = 0; i < count; ++i) {
DbConnectionInternal obj = _objectList[i];
if (null != obj) {
bool locked = false;
try {
locked = Monitor.TryEnter(obj);
if (locked) { // avoid race condition with PrePush/PostPop and IsEmancipated
if (obj.IsEmancipated) {
// Inside the lock, we want to do as little
// as possible, so we simply mark the object
// as being in the pool, but hand it off to
// an out of pool list to be deactivated,
// etc.
obj.PrePush(null);
reclaimedObjects.Add(obj);
}
}
}
finally {
if (locked)
Monitor.Exit(obj);
}
}
}
}
// NOTE: we don't want to call DeactivateObject while we're locked,
// because it can make roundtrips to the server and this will block
// object creation in the pooler. Instead, we queue things we need
// to do up, and process them outside the lock.
count = reclaimedObjects.Count;
for (int i = 0; i < count; ++i) {
DbConnectionInternal obj = reclaimedObjects[i];
Bid.PoolerTrace(" %d#, Connection %d#, Reclaiming.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfReclaimedConnections.Increment();
emancipatedObjectFound = true;
// NOTE: it is not possible for us to have a connection that has
// a delegated transaction at this point, because IsEmancipated
// would not have returned true if it did, and when a connection
// is emancipated, you can't enlist in a transaction (because you
// can't get to it to make the call...)
DeactivateObject(obj);
}
return emancipatedObjectFound;
}
internal void Startup() {
Bid.PoolerTrace(" %d#, CleanupWait=%d\n", ObjectID, _cleanupWait);
_cleanupTimer = CreateCleanupTimer();
if (NeedToReplenish) {
QueuePoolCreateRequest();
}
}
internal void Shutdown() {
Bid.PoolerTrace(" %d#\n", ObjectID);
_state = State.ShuttingDown;
Timer t; // deactivate timer callbacks
t = _cleanupTimer;
_cleanupTimer = null;
if (null != t) {
t.Dispose();
}
t = _errorTimer;
_errorTimer = null;
if (null != t) {
t.Dispose();
}
}
internal void TransactionBegin(SysTx.Transaction transaction) {
TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool;
if (null != transactedConnectionPool) {
transactedConnectionPool.TransactionBegin(transaction);
}
}
internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
Debug.Assert(null != transaction, "null transaction?");
Debug.Assert(null != transactedObject, "null transactedObject?");
// Note: connection may still be associated with transaction due to Explicit Unbinding requirement.
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
// called by the internal connection when it get's told that the
// transaction is completed. We tell the transacted pool to remove
// the connection from it's list, then we put the connection back in
// general circulation.
TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool;
if (null != transactedConnectionPool) {
transactedConnectionPool.TransactionEnded(transaction, transactedObject);
}
}
private DbConnectionInternal UserCreateRequest(DbConnection owningObject) {
// called by user when they were not able to obtain a free object but
// instead obtained creation mutex
DbConnectionInternal obj = null;
if (ErrorOccurred) {
throw _resError;
}
else {
if ((Count < MaxPoolSize) || (0 == MaxPoolSize)) {
// 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 ((Count & 0x1) == 0x1 || !ReclaimEmancipatedObjects())
obj = CreateObject(owningObject);
}
return obj;
}
}
private class DbConnectionInternalListStack {
private DbConnectionInternal _stack;
#if DEBUG
private int _version;
private int _count;
#endif
internal DbConnectionInternalListStack() {
}
internal int Count {
get {
int count = 0;
lock(this) {
for(DbConnectionInternal x = _stack; null != x; x = x.NextPooledObject) {
++count;
}
}
#if DEBUG
Debug.Assert(count == _count, "count is corrupt");
#endif
return count;
}
}
internal DbConnectionInternal SynchronizedPop() {
DbConnectionInternal value;
lock(this) {
value = _stack;
if (null != value) {
_stack = value.NextPooledObject;
value.NextPooledObject = null;
#if DEBUG
_version++;
_count--;
#endif
}
#if DEBUG
Debug.Assert((null != value || 0 == _count) && (0 <= _count), "broken SynchronizedPop");
#endif
}
return value;
}
internal void SynchronizedPush(DbConnectionInternal value) {
Debug.Assert(null != value, "pushing null value");
lock(this) {
#if DEBUG
Debug.Assert(null == value.NextPooledObject, "pushing value with non-null NextPooledObject");
int index = 0;
for(DbConnectionInternal x = _stack; null != x; x = x.NextPooledObject, ++index) {
Debug.Assert(x != value, "double push: connection already in stack");
}
Debug.Assert(_count == index, "SynchronizedPush count is corrupt");
#endif
value.NextPooledObject = _stack;
_stack = value;
#if DEBUG
_version++;
_count++;
#endif
}
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// [....]
//-----------------------------------------------------------------------------
namespace System.Data.ProviderBase {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Threading;
using SysTx = System.Transactions;
sealed internal class DbConnectionPool {
private enum State {
Initializing,
Running,
ShuttingDown,
}
internal const Bid.ApiGroup PoolerTracePoints = (Bid.ApiGroup)0x1000;
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// [PS SQL Hotfix RFC # 50003517: Current Transaction is disposed within the transaction scope,
// before leaving TransactionScope block against SQL Server 2008]
// 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 SysTx.Transaction _transaction;
internal TransactedConnectionList(int initialAllocation, SysTx.Transaction tx) : base(initialAllocation) {
_transaction = tx;
}
internal void Dispose() {
if (null != _transaction) {
_transaction.Dispose();
}
}
}
sealed private class TransactedConnectionPool : Hashtable {
DbConnectionPool _pool;
private static int _objectTypeCount; // Bid counter
internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
internal TransactedConnectionPool(DbConnectionPool pool) : base() {
Debug.Assert(null != pool, "null pool?");
_pool = pool;
Bid.PoolerTrace(" %d#, Constructed for connection pool %d#\n", ObjectID, _pool.ObjectID);
}
internal int ObjectID {
get {
return _objectID;
}
}
internal DbConnectionPool Pool {
get {
return _pool;
}
}
internal DbConnectionInternal GetTransactedObject(SysTx.Transaction transaction) {
Debug.Assert(null != transaction, "null transaction?");
DbConnectionInternal transactedObject = null;
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// Back porting a partial fix for Transaction Dispose issue observed in Dev10.
// Rest of the fix including the synchronization issue will be addressed the any subsequent release.
if (null != connections) {
lock (connections) {
int i = connections.Count - 1;
if (0 <= i) {
transactedObject = connections[i];
connections.RemoveAt(i);
}
}
}
if (null != transactedObject) {
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Popped.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
}
return transactedObject;
}
internal void PutTransactedObject(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
Debug.Assert(null != transaction, "null transaction?");
Debug.Assert(null != transactedObject, "null transactedObject?");
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Pushing.\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
// NOTE: it is possible that the connecton was put on the
// deactivate queue, and while it was on the queue, the
// transaction ended, causing the list for the transaction
// to have been removed. In that case, we can't expect
// the list to be here.
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// Back porting a partial fix for Transaction Dispose issue observed in Dev10.
// Rest of the fix including the synchronization issue will be addressed the any subsequent release.
if (null != connections) {
lock (connections) {
Debug.Assert(0 > connections.IndexOf(transactedObject), "adding to pool a second time?");
connections.Add(transactedObject);
Pool.PerformanceCounters.NumberOfFreeConnections.Increment();
}
}
}
internal void TransactionBegin(SysTx.Transaction transaction) {
Bid.PoolerTrace(" %d#, Transaction %d#, Begin.\n", ObjectID, transaction.GetHashCode());
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
if (null == connections) {
SysTx.Transaction transactionClone = null;
try {
transactionClone = transaction.Clone();
TransactedConnectionList newConnections = new TransactedConnectionList(2, transactionClone); // start with only two connections in the list; most times we won't need that many.
Bid.PoolerTrace(" %d#, Transaction %d#, Adding List to transacted pool.\n", ObjectID, transaction.GetHashCode());
lock (this) {
connections = (TransactedConnectionList)this[transactionClone];
if (null == connections) {
connections = newConnections;
this.Add(transactionClone, connections);
transactionClone = null; // we've used it -- don't throw it away.
}
}
}
finally {
if (null != transactionClone) {
transactionClone.Dispose();
}
}
Bid.PoolerTrace(" %d#, Transaction %d#, Added.\n", ObjectID, transaction.GetHashCode());
}
}
internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
TransactedConnectionList connections = (TransactedConnectionList)this[transaction];
int entry = -1;
// NOTE: we may be ending a transaction for a connection that is
// currently not in the pool, and therefore it may not have
// a list for it, because it may have been removed already.
// Hotfix: SQLBUVSTS01 # 272199: Hotfix request for RFC #50003517
// Back porting a partial fix for Transaction Dispose issue observed in Dev10.
// Rest of the fix including the synchronization issue will be addressed the any subsequent release.
if (null != connections) {
lock (connections) {
entry = connections.IndexOf(transactedObject);
if (entry >= 0) {
connections.RemoveAt(entry);
}
// Once we've completed all the ended notifications, we can
// safely remove the list from the transacted pool.
if (0 >= connections.Count) {
Bid.PoolerTrace(" %d#, Transaction %d#, Removing List from transacted pool.\n", ObjectID, transaction.GetHashCode());
lock (this) {
Remove(transaction);
}
Bid.PoolerTrace(" %d#, Transaction %d#, Removed.\n", ObjectID, transaction.GetHashCode());
// we really need to dispose our connection list; it may have
// native resources via the tx and GC may not happen soon enough.
connections.Dispose();
}
}
}
// If (and only if) we found the connection in the list of
// connections, we'll put it back...
if (0 <= entry) {
Pool.PerformanceCounters.NumberOfFreeConnections.Decrement();
Pool.PutObjectFromTransactedPool(transactedObject);
}
}
}
private sealed class PoolWaitHandles : DbBuffer {
private readonly Semaphore _poolSemaphore;
private readonly ManualResetEvent _errorEvent;
// Using a Mutex requires ThreadAffinity because SQL CLR can swap
// the underlying Win32 thread associated with a managed thread in preemptive mode.
// Using an AutoResetEvent does not have that complication.
private readonly Semaphore _creationSemaphore;
private readonly SafeHandle _poolHandle;
private readonly SafeHandle _errorHandle;
private readonly SafeHandle _creationHandle;
private readonly int _releaseFlags;
internal PoolWaitHandles(Semaphore poolSemaphore, ManualResetEvent errorEvent, Semaphore creationSemaphore) : base(3*IntPtr.Size) {
bool mustRelease1 = false, mustRelease2 = false, mustRelease3 = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
_poolSemaphore = poolSemaphore;
_errorEvent = errorEvent;
_creationSemaphore = creationSemaphore;
// because SafeWaitHandle doesn't have reliability contract
_poolHandle = poolSemaphore.SafeWaitHandle;
_errorHandle = errorEvent.SafeWaitHandle;
_creationHandle = creationSemaphore.SafeWaitHandle;
_poolHandle.DangerousAddRef(ref mustRelease1);
_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");
WriteIntPtr(SEMAPHORE_HANDLE*IntPtr.Size, _poolHandle.DangerousGetHandle());
WriteIntPtr(ERROR_HANDLE*IntPtr.Size, _errorHandle.DangerousGetHandle());
WriteIntPtr(CREATION_HANDLE*IntPtr.Size, _creationHandle.DangerousGetHandle());
}
finally {
if (mustRelease1) {
_releaseFlags |= 1;
}
if (mustRelease2) {
_releaseFlags |= 2;
}
if (mustRelease3) {
_releaseFlags |= 4;
}
}
}
internal SafeHandle CreationHandle {
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
get { return _creationHandle; }
}
internal Semaphore CreationSemaphore {
get { return _creationSemaphore; }
}
internal ManualResetEvent ErrorEvent {
get { return _errorEvent; }
}
internal Semaphore PoolSemaphore {
get { return _poolSemaphore; }
}
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)) {
_poolHandle.DangerousRelease();
}
if (0 != (2 & _releaseFlags)) {
_errorHandle.DangerousRelease();
}
if (0 != (4 & _releaseFlags)) {
_creationHandle.DangerousRelease();
}
return base.ReleaseHandle();
}
}
private const int MAX_Q_SIZE = (int)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 WAIT_OBJECT_0 = 0;
private const int WAIT_TIMEOUT = (int)0x102;
private const int WAIT_ABANDONED = (int)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 readonly int _cleanupWait;
private readonly DbConnectionPoolIdentity _identity;
private readonly DbConnectionFactory _connectionFactory;
private readonly DbConnectionPoolGroup _connectionPoolGroup;
private readonly DbConnectionPoolGroupOptions _connectionPoolGroupOptions;
private DbConnectionPoolProviderInfo _connectionPoolProviderInfo;
private State _state;
private readonly DbConnectionInternalListStack _stackOld = new DbConnectionInternalListStack();
private readonly DbConnectionInternalListStack _stackNew = new DbConnectionInternalListStack();
private readonly WaitCallback _poolCreateRequest;
private readonly Queue _deactivateQueue;
private readonly WaitCallback _deactivateCallback;
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; // Bid counter
internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
// only created by DbConnectionPoolGroup.GetConnectionPool
internal DbConnectionPool(
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");
if ((null != identity) && identity.IsRestricted) {
throw ADP.InternalError(ADP.InternalErrorCode.AttemptingToPoolOnRestrictedToken);
}
_state= State.Initializing;
lock(_random) { // Random.Next is not thread-safe
_cleanupWait = _random.Next(12, 24)*10*1000; // 2-4 minutes in 10 sec intervals, WebData 103603
}
_connectionFactory = connectionFactory;
_connectionPoolGroup = connectionPoolGroup;
_connectionPoolGroupOptions = connectionPoolGroup.PoolGroupOptions;
_connectionPoolProviderInfo = connectionPoolProviderInfo;
_identity = identity;
if (UseDeactivateQueue) {
_deactivateQueue = new Queue();
_deactivateCallback = new WaitCallback(ProcessDeactivateQueue);
}
_waitHandles = new PoolWaitHandles(
new Semaphore(0, MAX_Q_SIZE),
new ManualResetEvent(false),
new Semaphore(1, 1));
_errorWait = ERROR_WAIT_DEFAULT;
_errorTimer = null; // No error yet.
_objectList = new List(MaxPoolSize);
if(ADP.IsPlatformNT5) {
_transactedConnectionPool = new TransactedConnectionPool(this);
}
_poolCreateRequest = new WaitCallback(PoolCreateRequest); // used by CleanupCallback
_state = State.Running;
Bid.PoolerTrace(" %d#, Constructed.\n", ObjectID);
//_cleanupTimer & QueuePoolCreateRequest is delayed until DbConnectionPoolGroup calls
// StartBackgroundCallbacks after pool is actually in the collection
}
private int CreationTimeout {
get { return PoolGroupOptions.CreationTimeout; }
}
internal int Count {
get { return _totalObjects; }
}
internal DbConnectionFactory ConnectionFactory {
get { return _connectionFactory; }
}
internal bool ErrorOccurred {
get { return _errorOccurred; }
}
private bool HasTransactionAffinity {
get { return PoolGroupOptions.HasTransactionAffinity; }
}
internal TimeSpan LoadBalanceTimeout {
get { return PoolGroupOptions.LoadBalanceTimeout; }
}
private bool NeedToReplenish {
get {
if (State.Running != _state) // SQL BU DT 364595 - 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 waitingRequests = _waitCount;
bool needToReplenish = (freeObjects < waitingRequests) || ((freeObjects == waitingRequests) && (totalObjects > 1));
return needToReplenish;
}
}
internal DbConnectionPoolIdentity Identity {
get { return _identity; }
}
internal bool IsRunning {
get { return State.Running == _state; }
}
private int MaxPoolSize {
get { return PoolGroupOptions.MaxPoolSize; }
}
private int MinPoolSize {
get { return PoolGroupOptions.MinPoolSize; }
}
internal int ObjectID {
get {
return _objectID;
}
}
internal DbConnectionPoolCounters PerformanceCounters {
get { return _connectionFactory.PerformanceCounters; }
}
internal DbConnectionPoolGroup PoolGroup {
get { return _connectionPoolGroup; }
}
internal DbConnectionPoolGroupOptions PoolGroupOptions {
get { return _connectionPoolGroupOptions; }
}
internal DbConnectionPoolProviderInfo ProviderInfo {
get { return _connectionPoolProviderInfo; }
}
private bool UseDeactivateQueue {
get { return PoolGroupOptions.UseDeactivateQueue; }
}
internal bool UseLoadBalancing {
get { return PoolGroupOptions.UseLoadBalancing; }
}
private bool UsingIntegrateSecurity {
get { return (null != _identity && DbConnectionPoolIdentity.NoIdentity != _identity); }
}
private void CleanupCallback(Object state) {
// Called when the cleanup-timer ticks over.
// This is the automatic prunning method. Every period, we will
// perform a two-step process:
//
// First, for each free object above MinPoolSize, we will obtain a
// semaphore representing one object and destroy one from old stack.
// We will continue this until we either reach MinPoolSize, we are
// unable to obtain a free object, or we have exhausted all the
// objects on the old stack.
//
// Second we move all free objects on the new stack to the old stack.
// So, every period the objects on the old stack are destroyed and
// the objects on the new stack are pushed to the old stack. All
// objects that are currently out and in use are not on either stack.
//
// With this logic, objects are pruned from the pool if unused for
// at least one period but not more than two periods.
Bid.PoolerTrace(" %d#\n", ObjectID);
// Destroy free objects that put us above MinPoolSize from old stack.
while(Count > MinPoolSize) { // While above MinPoolSize...
if (_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
// We obtained a objects from the semaphore.
DbConnectionInternal obj = _stackOld.SynchronizedPop();
if (null != obj) {
// If we obtained one from the old stack, destroy it.
PerformanceCounters.NumberOfFreeConnections.Decrement();
// Transaction roots must survive even aging out (TxEnd event will clean them up).
bool shouldDestroy = true;
lock (obj) { // Lock to prevent ---- window between IsTransactionRoot and shouldDestroy assignment
if (obj.IsTransactionRoot) {
shouldDestroy = false;
}
}
// !!!!!!!!!! WARNING !!!!!!!!!!!!!
// ONLY touch obj after lock release if shouldDestroy is false!!! Otherwise, it may be destroyed
// by transaction-end thread!
// Note that there is a minor race condition between this task and the transaction end event, if the latter runs
// between the lock above and the SetInStasis call below. The reslult is that the stasis counter may be
// incremented without a corresponding decrement (the transaction end task is normally expected
// to decrement, but will only do so if the stasis flag is set when it runs). I've minimized the size
// of the window, but we aren't totally eliminating it due to SetInStasis needing to do bid tracing, which
// we don't want to do under this lock, if possible. It should be possible to eliminate this ---- with
// more substantial re-architecture of the pool, but we don't have the time to do that work for the current release.
if (shouldDestroy) {
DestroyObject(obj);
}
else {
obj.SetInStasis();
}
}
else {
// Else we exhausted the old stack (the object the
// semaphore represents is on the new stack), so break.
_waitHandles.PoolSemaphore.Release(1);
break;
}
}
else {
break;
}
}
// Push to the old-stack. For each free object, move object from
// new stack to old stack.
if(_waitHandles.PoolSemaphore.WaitOne(0, false) /* != WAIT_TIMEOUT */) {
for(;;) {
DbConnectionInternal obj = _stackNew.SynchronizedPop();
if (null == obj)
break;
Bid.PoolerTrace(" %d#, ChangeStacks=%d#\n", ObjectID, obj.ObjectID);
Debug.Assert(!obj.IsEmancipated, "pooled object not in pool");
Debug.Assert(obj.CanBePooled, "pooled object is not poolable");
_stackOld.SynchronizedPush(obj);
}
_waitHandles.PoolSemaphore.Release(1);
}
// Queue up a request to bring us up to MinPoolSize
QueuePoolCreateRequest();
}
internal void Clear() {
Bid.PoolerTrace(" %d#, Clearing.\n", ObjectID);
DbConnectionInternal obj;
// First, quickly doom everything.
lock(_objectList) {
int count = _objectList.Count;
for (int i = 0; i < count; ++i) {
obj = _objectList[i];
if (null != obj) {
obj.DoNotPoolThisConnection();
}
}
}
// Second, dispose of all the free connections.
while (null != (obj = _stackNew.SynchronizedPop())) {
PerformanceCounters.NumberOfFreeConnections.Decrement();
DestroyObject(obj);
}
while (null != (obj = _stackOld.SynchronizedPop())) {
PerformanceCounters.NumberOfFreeConnections.Decrement();
DestroyObject(obj);
}
// Finally, reclaim everything that's emancipated (which, because
// it's been doomed, will cause it to be disposed of as well)
ReclaimEmancipatedObjects();
Bid.PoolerTrace(" %d#, Cleared.\n", ObjectID);
}
private Timer CreateCleanupTimer() {
return (new Timer(new TimerCallback(this.CleanupCallback), null, _cleanupWait, _cleanupWait));
}
private DbConnectionInternal CreateObject(DbConnection owningObject) {
DbConnectionInternal newObj = null;
try {
newObj = _connectionFactory.CreatePooledConnection(owningObject, this, _connectionPoolGroup.ConnectionOptions);
if (null == newObj) {
throw ADP.InternalError(ADP.InternalErrorCode.CreateObjectReturnedNull); // CreateObject succeeded, but null object
}
if (!newObj.CanBePooled) {
throw ADP.InternalError(ADP.InternalErrorCode.NewObjectCannotBePooled); // CreateObject succeeded, but non-poolable object
}
newObj.PrePush(null);
lock (_objectList) {
_objectList.Add(newObj);
_totalObjects = _objectList.Count;
PerformanceCounters.NumberOfPooledConnections.Increment(); //
}
Bid.PoolerTrace(" %d#, Connection %d#, Added to pool.\n", ObjectID, newObj.ObjectID);
// Reset the error wait:
_errorWait = ERROR_WAIT_DEFAULT;
}
catch(Exception e) {
//
if (!ADP.IsCatchableExceptionType(e)) {
throw;
}
ADP.TraceExceptionForCapture(e);
newObj = null; // set to null, so we do not return bad new object
// Failed to create instance
_resError = e;
_waitHandles.ErrorEvent.Set();
_errorOccurred = true;
_errorTimer = new Timer(new TimerCallback(this.ErrorCallback), null, _errorWait, _errorWait);
if (30000 < _errorWait) {
_errorWait = 60000;
}
else {
_errorWait *= 2;
}
throw;
}
return newObj;
}
private void DeactivateObject(DbConnectionInternal obj) {
Bid.PoolerTrace(" %d#, Connection %d#, Deactivating.\n", ObjectID, obj.ObjectID);
obj.DeactivateConnection(); // we presume this operation is safe outside of a lock...
if ((State.Running == _state) && !obj.IsConnectionDoomed) {
bool returnToGeneralPool = true;
lock (obj) {
// A connection with a delegated transaction cannot currently
// be returned to a different customer until the transaction
// actually completes, so we send it into Stasis -- the SysTx
// transaction object will ensure that it is owned (not lost),
// and it will be certain to put it back into the pool.
if (obj.IsNonPoolableTransactionRoot) {
obj.SetInStasis();
returnToGeneralPool = false;
}
else {
// We must put this connection into the transacted pool
// while inside a lock to prevent a race condition with
// the transaction asyncronously completing on a second
// thread.
SysTx.Transaction transaction = obj.EnlistedTransaction;
if (null != transaction) {
TransactionBegin(transaction); // Delayed creation of transacted pool
_transactedConnectionPool.PutTransactedObject(transaction, obj);
returnToGeneralPool = false;
}
}
}
// Only push the connection into the general pool if we didn't
// already push it onto the transacted pool.
if (returnToGeneralPool) {
PutNewObject(obj);
}
}
else {
// the object is not fit for reuse -- just dispose of it.
DestroyObject(obj);
QueuePoolCreateRequest();
}
}
private void DestroyObject(DbConnectionInternal obj) {
// A connection with a delegated transaction cannot be disposed of
// until the delegated transaction has actually completed. Instead,
// we simply leave it alone; when the transaction completes, it will
// come back through PutObjectFromTransactedPool, which will call us
// again.
if (obj.IsTxRootWaitingForTxEnd) {
Bid.PoolerTrace(" %d#, Connection %d#, Has Delegated Transaction, waiting to Dispose.\n", ObjectID, obj.ObjectID);
}
else {
Bid.PoolerTrace(" %d#, Connection %d#, Removing from pool.\n", ObjectID, obj.ObjectID);
bool removed = false;
lock (_objectList) {
removed = _objectList.Remove(obj);
Debug.Assert(removed, "attempt to DestroyObject not in list");
_totalObjects = _objectList.Count;
}
if (removed) {
Bid.PoolerTrace(" %d#, Connection %d#, Removed from pool.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfPooledConnections.Decrement();
}
obj.Dispose();
Bid.PoolerTrace(" %d#, Connection %d#, Disposed.\n", ObjectID, obj.ObjectID);
PerformanceCounters.HardDisconnectsPerSecond.Increment();
}
}
private void ErrorCallback(Object state) {
Bid.PoolerTrace(" %d#, Resetting Error handling.\n", ObjectID);
_errorOccurred = false;
_waitHandles.ErrorEvent.Reset();
Timer t = _errorTimer;
_errorTimer = null;
if (t != null) {
t.Dispose(); // Cancel timer request.
}
}
internal DbConnectionInternal GetConnection(DbConnection owningObject) {
DbConnectionInternal obj = null;
SysTx.Transaction transaction = null;
PerformanceCounters.SoftConnectsPerSecond.Increment();
if(_state != State.Running) {
Bid.PoolerTrace(" %d#, DbConnectionInternal State != Running.\n", ObjectID);
return null;
}
Bid.PoolerTrace(" %d#, Getting connection.\n", ObjectID);
// If automatic transaction enlistment is required, then we try to
// get the connection from the transacted connection pool first.
if (HasTransactionAffinity) {
obj = GetFromTransactedPool(out transaction);
}
if (null == obj) {
Interlocked.Increment(ref _waitCount);
uint waitHandleCount = 3;
uint timeout = (uint)CreationTimeout;
do {
int waitResult = BOGUS_HANDLE;
int releaseSemaphoreResult = 0;
bool mustRelease = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
_waitHandles.DangerousAddRef(ref mustRelease);
// We absolutely must have the value of waitResult set,
// or we may leak the mutex in async abort cases.
RuntimeHelpers.PrepareConstrainedRegions();
try {
Debug.Assert(2 == waitHandleCount || 3 == waitHandleCount, "unexpected waithandle count");
}
finally {
waitResult = SafeNativeMethods.WaitForMultipleObjectsEx(waitHandleCount, _waitHandles.DangerousGetHandle(), false, timeout, false);
}
// From the WaitAny docs: "If more than one object became signaled during
// the call, this is the array index of the signaled object with the
// smallest index value of all the signaled objects." This is important
// so that the free object signal will be returned before a creation
// signal.
switch (waitResult) {
case WAIT_TIMEOUT:
Bid.PoolerTrace(" %d#, Wait timed out.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
return null;
case ERROR_HANDLE:
// Throw the error that PoolCreateRequest stashed.
Bid.PoolerTrace(" %d#, Errors are set.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw _resError;
case CREATION_HANDLE:
Bid.PoolerTrace(" %d#, Creating new connection.\n", ObjectID);
try {
obj = UserCreateRequest(owningObject);
}
catch {
if (null == obj) {
Interlocked.Decrement(ref _waitCount);
}
throw;
}
finally {
// SQLBUDT #386664 - ensure that we release this waiter, regardless
// of any exceptions that may be thrown.
if (null != obj) {
Interlocked.Decrement(ref _waitCount);
}
}
if (null == obj) {
// 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.
//
if (Count >= MaxPoolSize && 0 != MaxPoolSize) {
if (!ReclaimEmancipatedObjects()) {
// modify handle array not to wait on creation mutex anymore
Debug.Assert(2 == CREATION_HANDLE, "creation handle changed value");
waitHandleCount = 2;
}
}
}
break;
case SEMAPHORE_HANDLE:
//
// guaranteed available inventory
//
Interlocked.Decrement(ref _waitCount);
obj = GetFromGeneralPool();
break;
case WAIT_FAILED:
Bid.PoolerTrace(" %d#, Wait failed.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
goto default; // if ThrowExceptionForHR didn't throw for some reason
case (WAIT_ABANDONED+SEMAPHORE_HANDLE):
Bid.PoolerTrace(" %d#, Semaphore handle abandonded.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw new AbandonedMutexException(SEMAPHORE_HANDLE,_waitHandles.PoolSemaphore);
case (WAIT_ABANDONED+ERROR_HANDLE):
Bid.PoolerTrace(" %d#, Error handle abandonded.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw new AbandonedMutexException(ERROR_HANDLE,_waitHandles.ErrorEvent);
case (WAIT_ABANDONED+CREATION_HANDLE):
Bid.PoolerTrace(" %d#, Creation handle abandoned.\n", ObjectID);
Interlocked.Decrement(ref _waitCount);
throw new AbandonedMutexException(CREATION_HANDLE,_waitHandles.CreationSemaphore);
default:
Bid.PoolerTrace(" %d#, WaitForMultipleObjects=%d\n", ObjectID, waitResult);
Interlocked.Decrement(ref _waitCount);
throw ADP.InternalError(ADP.InternalErrorCode.UnexpectedWaitAnyResult);
}
}
finally {
if (CREATION_HANDLE == waitResult) {
int result = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
if (0 == result) { // failure case
releaseSemaphoreResult = Marshal.GetHRForLastWin32Error();
}
}
if (mustRelease) {
_waitHandles.DangerousRelease();
}
}
if (0 != releaseSemaphoreResult) {
Marshal.ThrowExceptionForHR(releaseSemaphoreResult); // will only throw if (hresult < 0)
}
} while (null == obj);
}
if (null != obj) {
lock (obj) { // Protect against Clear and ReclaimEmancipatedObjects, which call IsEmancipated, which is affected by PrePush and PostPop
obj.PostPop(owningObject);
}
try {
obj.ActivateConnection(transaction);
}
catch(SecurityException) {
// if Activate throws an exception
// put it back in the pool or have it properly disposed of
this.PutObject(obj, owningObject);
throw;
}
}
return(obj);
}
private DbConnectionInternal GetFromGeneralPool() {
DbConnectionInternal obj = null;
obj = _stackNew.SynchronizedPop();
if (null == obj) {
obj = _stackOld.SynchronizedPop();
}
// SQLBUDT #356870 -- 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!");
if (null != obj) {
Bid.PoolerTrace(" %d#, Connection %d#, Popped from general pool.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfFreeConnections.Decrement();
}
return(obj);
}
private DbConnectionInternal GetFromTransactedPool(out SysTx.Transaction transaction) {
transaction = ADP.GetCurrentTransaction();
DbConnectionInternal obj = null;
if (null != transaction && null != _transactedConnectionPool) {
obj = _transactedConnectionPool.GetTransactedObject(transaction);
if (null != obj) {
Bid.PoolerTrace(" %d#, Connection %d#, Popped from transacted pool.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfFreeConnections.Decrement();
}
}
return obj;
}
private void PoolCreateRequest(object state) {
// called by pooler to ensure pool requests are currently being satisfied -
// creation mutex has not been obtained
IntPtr hscp;
Bid.PoolerScopeEnter(out hscp, " %d#\n", ObjectID);
try {
if (State.Running == _state) {
// Before creating any new objects, reclaim any released objects that were
// not closed.
ReclaimEmancipatedObjects();
if (!ErrorOccurred) {
if (NeedToReplenish) {
// Check to see if pool was created using integrated security and if so, make
// sure the identity of current user matches that of user that created pool.
// If it doesn't match, do not create any objects on the ThreadPool thread,
// since either Open will fail or we will open a object for this pool that does
// not belong in this pool. The side effect of this is that if using integrated
// security min pool size cannot be guaranteed.
if (UsingIntegrateSecurity && !_identity.Equals(DbConnectionPoolIdentity.GetCurrent())) {
return;
}
bool mustRelease = false;
int waitResult = BOGUS_HANDLE;
uint timeout = (uint)CreationTimeout;
RuntimeHelpers.PrepareConstrainedRegions();
try {
_waitHandles.DangerousAddRef(ref mustRelease);
// Obtain creation mutex so we're the only one creating objects
// and we must have the wait result
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
waitResult = SafeNativeMethods.WaitForSingleObjectEx(_waitHandles.CreationHandle.DangerousGetHandle(), timeout, false);
}
if (WAIT_OBJECT_0 == waitResult) {
DbConnectionInternal newObj;
// Check ErrorOccurred again after obtaining mutex
if (!ErrorOccurred) {
while (NeedToReplenish) {
newObj = CreateObject((DbConnection)null);
// We do not need to check error flag here, since we know if
// CreateObject returned null, we are in error case.
if (null != newObj) {
PutNewObject(newObj);
}
else {
break;
}
}
}
}
else if (WAIT_TIMEOUT == waitResult) {
// do not wait forever and potential block this worker thread
// instead wait for a period of time and just requeue to try again
QueuePoolCreateRequest();
}
else {
// trace waitResult and ignore the failure
Bid.PoolerTrace(" %d#, PoolCreateRequest called WaitForSingleObject failed %d", ObjectID, waitResult);
}
}
catch (Exception e) {
//
if (!ADP.IsCatchableExceptionType(e)) {
throw;
}
// Now that CreateObject can throw, we need to catch the exception and discard it.
// There is no further action we can take beyond tracing. The error will be
// thrown to the user the next time they request a connection.
Bid.PoolerTrace(" %d#, PoolCreateRequest called CreateConnection which threw an exception: " + e.ToString(), ObjectID);
}
finally {
if (WAIT_OBJECT_0 == waitResult) {
// reuse waitResult and ignore its value
waitResult = SafeNativeMethods.ReleaseSemaphore(_waitHandles.CreationHandle.DangerousGetHandle(), 1, IntPtr.Zero);
}
if (mustRelease) {
_waitHandles.DangerousRelease();
}
}
}
}
}
}
finally {
Bid.ScopeLeave(ref hscp);
}
}
private void ProcessDeactivateQueue(object state) {
IntPtr hscp;
Bid.PoolerScopeEnter(out hscp, " %d#\n", ObjectID);
try {
object[] deactivateQueue;
lock (_deactivateQueue.SyncRoot) {
deactivateQueue = _deactivateQueue.ToArray();
_deactivateQueue.Clear();
}
foreach (DbConnectionInternal obj in deactivateQueue) {
PerformanceCounters.NumberOfStasisConnections.Decrement();
DeactivateObject(obj);
}
}
finally {
Bid.ScopeLeave(ref hscp);
}
}
internal void PutNewObject(DbConnectionInternal obj) {
Debug.Assert(null != obj, "why are we adding a null object to the pool?");
Debug.Assert(obj.CanBePooled, "non-poolable object in pool");
Bid.PoolerTrace(" %d#, Connection %d#, Pushing to general pool.\n", ObjectID, obj.ObjectID);
_stackNew.SynchronizedPush(obj);
_waitHandles.PoolSemaphore.Release(1);
PerformanceCounters.NumberOfFreeConnections.Increment();
}
internal void PutObject(DbConnectionInternal obj, object owningObject) {
Debug.Assert(null != obj, "null obj?");
PerformanceCounters.SoftDisconnectsPerSecond.Increment();
// Once a connection is closing (which is the state that we're in at
// this point in time) you cannot delegate a transaction to or enlist
// a transaction in it, so we can correctly presume that if there was
// not a delegated or enlisted transaction to start with, that there
// will not be a delegated or enlisted transaction once we leave the
// lock.
lock (obj) {
// Calling PrePush prevents the object from being reclaimed
// once we leave the lock, because it sets _pooledCount such
// that it won't appear to be out of the pool. What that
// means, is that we're now responsible for this connection:
// it won't get reclaimed if we drop the ball somewhere.
obj.PrePush(owningObject);
//
}
if (UseDeactivateQueue) {
// If we're using the DeactivateQueue, we'll just queue it up and
// be done; all the hard work will be done on the despooler thread.
bool needToQueueWorkItem;
Bid.PoolerTrace(" %d#, Connection %d#, Queueing for deactivation.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfStasisConnections.Increment();
lock (_deactivateQueue.SyncRoot) {
needToQueueWorkItem = (0 == _deactivateQueue.Count);
_deactivateQueue.Enqueue(obj);
}
if (needToQueueWorkItem) {
// Make sure we actually get around to deactivating the object
// and making it available again.
ThreadPool.QueueUserWorkItem(_deactivateCallback, null);
}
}
else {
// no deactivate queue -- do the work right now.
DeactivateObject(obj);
}
}
internal void PutObjectFromTransactedPool(DbConnectionInternal obj) {
Debug.Assert(null != obj, "null pooledObject?");
Debug.Assert(!obj.HasEnlistedTransaction, "pooledObject is still enlisted?");
// called by the transacted connection pool , once it's removed the
// connection from it's list. We put the connection back in general
// circulation.
// NOTE: there is no locking required here because if we're in this
// method, we can safely presume that the caller is the only person
// that is using the connection, and that all pre-push logic has been
// done and all transactions are ended.
Bid.PoolerTrace(" %d#, Connection %d#, Transaction has ended.\n", ObjectID, obj.ObjectID);
if (_state == State.Running && obj.CanBePooled) {
PutNewObject(obj);
}
else {
DestroyObject(obj);
QueuePoolCreateRequest();
}
}
private void QueuePoolCreateRequest() {
if (State.Running == _state) {
// Make sure we're at quota by posting a callback to the threadpool.
ThreadPool.QueueUserWorkItem(_poolCreateRequest);
}
}
private bool ReclaimEmancipatedObjects() {
bool emancipatedObjectFound = false;
Bid.PoolerTrace(" %d#\n", ObjectID);
List reclaimedObjects = new List();
int count;
lock(_objectList) {
count = _objectList.Count;
for (int i = 0; i < count; ++i) {
DbConnectionInternal obj = _objectList[i];
if (null != obj) {
bool locked = false;
try {
locked = Monitor.TryEnter(obj);
if (locked) { // avoid race condition with PrePush/PostPop and IsEmancipated
if (obj.IsEmancipated) {
// Inside the lock, we want to do as little
// as possible, so we simply mark the object
// as being in the pool, but hand it off to
// an out of pool list to be deactivated,
// etc.
obj.PrePush(null);
reclaimedObjects.Add(obj);
}
}
}
finally {
if (locked)
Monitor.Exit(obj);
}
}
}
}
// NOTE: we don't want to call DeactivateObject while we're locked,
// because it can make roundtrips to the server and this will block
// object creation in the pooler. Instead, we queue things we need
// to do up, and process them outside the lock.
count = reclaimedObjects.Count;
for (int i = 0; i < count; ++i) {
DbConnectionInternal obj = reclaimedObjects[i];
Bid.PoolerTrace(" %d#, Connection %d#, Reclaiming.\n", ObjectID, obj.ObjectID);
PerformanceCounters.NumberOfReclaimedConnections.Increment();
emancipatedObjectFound = true;
// NOTE: it is not possible for us to have a connection that has
// a delegated transaction at this point, because IsEmancipated
// would not have returned true if it did, and when a connection
// is emancipated, you can't enlist in a transaction (because you
// can't get to it to make the call...)
DeactivateObject(obj);
}
return emancipatedObjectFound;
}
internal void Startup() {
Bid.PoolerTrace(" %d#, CleanupWait=%d\n", ObjectID, _cleanupWait);
_cleanupTimer = CreateCleanupTimer();
if (NeedToReplenish) {
QueuePoolCreateRequest();
}
}
internal void Shutdown() {
Bid.PoolerTrace(" %d#\n", ObjectID);
_state = State.ShuttingDown;
Timer t; // deactivate timer callbacks
t = _cleanupTimer;
_cleanupTimer = null;
if (null != t) {
t.Dispose();
}
t = _errorTimer;
_errorTimer = null;
if (null != t) {
t.Dispose();
}
}
internal void TransactionBegin(SysTx.Transaction transaction) {
TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool;
if (null != transactedConnectionPool) {
transactedConnectionPool.TransactionBegin(transaction);
}
}
internal void TransactionEnded(SysTx.Transaction transaction, DbConnectionInternal transactedObject) {
Debug.Assert(null != transaction, "null transaction?");
Debug.Assert(null != transactedObject, "null transactedObject?");
// Note: connection may still be associated with transaction due to Explicit Unbinding requirement.
Bid.PoolerTrace(" %d#, Transaction %d#, Connection %d#, Transaction Completed\n", ObjectID, transaction.GetHashCode(), transactedObject.ObjectID);
// called by the internal connection when it get's told that the
// transaction is completed. We tell the transacted pool to remove
// the connection from it's list, then we put the connection back in
// general circulation.
TransactedConnectionPool transactedConnectionPool = _transactedConnectionPool;
if (null != transactedConnectionPool) {
transactedConnectionPool.TransactionEnded(transaction, transactedObject);
}
}
private DbConnectionInternal UserCreateRequest(DbConnection owningObject) {
// called by user when they were not able to obtain a free object but
// instead obtained creation mutex
DbConnectionInternal obj = null;
if (ErrorOccurred) {
throw _resError;
}
else {
if ((Count < MaxPoolSize) || (0 == MaxPoolSize)) {
// 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 ((Count & 0x1) == 0x1 || !ReclaimEmancipatedObjects())
obj = CreateObject(owningObject);
}
return obj;
}
}
private class DbConnectionInternalListStack {
private DbConnectionInternal _stack;
#if DEBUG
private int _version;
private int _count;
#endif
internal DbConnectionInternalListStack() {
}
internal int Count {
get {
int count = 0;
lock(this) {
for(DbConnectionInternal x = _stack; null != x; x = x.NextPooledObject) {
++count;
}
}
#if DEBUG
Debug.Assert(count == _count, "count is corrupt");
#endif
return count;
}
}
internal DbConnectionInternal SynchronizedPop() {
DbConnectionInternal value;
lock(this) {
value = _stack;
if (null != value) {
_stack = value.NextPooledObject;
value.NextPooledObject = null;
#if DEBUG
_version++;
_count--;
#endif
}
#if DEBUG
Debug.Assert((null != value || 0 == _count) && (0 <= _count), "broken SynchronizedPop");
#endif
}
return value;
}
internal void SynchronizedPush(DbConnectionInternal value) {
Debug.Assert(null != value, "pushing null value");
lock(this) {
#if DEBUG
Debug.Assert(null == value.NextPooledObject, "pushing value with non-null NextPooledObject");
int index = 0;
for(DbConnectionInternal x = _stack; null != x; x = x.NextPooledObject, ++index) {
Debug.Assert(x != value, "double push: connection already in stack");
}
Debug.Assert(_count == index, "SynchronizedPush count is corrupt");
#endif
value.NextPooledObject = _stack;
_stack = value;
#if DEBUG
_version++;
_count++;
#endif
}
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- KeyGestureValueSerializer.cs
- CompModSwitches.cs
- COM2PropertyPageUITypeConverter.cs
- TextEditorSelection.cs
- TextTabProperties.cs
- RijndaelCryptoServiceProvider.cs
- XmlSchemaParticle.cs
- WebPartTransformer.cs
- RemoteArgument.cs
- Privilege.cs
- ResizeGrip.cs
- AutomationProperties.cs
- ProxyManager.cs
- EnumerableCollectionView.cs
- InternalCache.cs
- DataGridViewCellFormattingEventArgs.cs
- AutomationPatternInfo.cs
- DeviceSpecificDesigner.cs
- ACL.cs
- DeploymentSectionCache.cs
- RightsManagementInformation.cs
- KeyValueConfigurationCollection.cs
- TrackingCondition.cs
- StickyNoteAnnotations.cs
- ConnectionStringsExpressionBuilder.cs
- COM2FontConverter.cs
- EdgeProfileValidation.cs
- Exceptions.cs
- ContainsRowNumberChecker.cs
- VirtualDirectoryMapping.cs
- ChangeConflicts.cs
- TaskHelper.cs
- BrowserCapabilitiesFactoryBase.cs
- HelpOperationInvoker.cs
- messageonlyhwndwrapper.cs
- TemplateColumn.cs
- DiscoveryClientProtocol.cs
- BufferedReceiveManager.cs
- PrintPreviewDialog.cs
- ObjectItemCachedAssemblyLoader.cs
- PresentationTraceSources.cs
- BrowsableAttribute.cs
- PaperSource.cs
- SQLBoolean.cs
- LocalizationComments.cs
- UDPClient.cs
- LabelAutomationPeer.cs
- PrePrepareMethodAttribute.cs
- ReferenceSchema.cs
- CssClassPropertyAttribute.cs
- DataRelationPropertyDescriptor.cs
- DecimalStorage.cs
- ExpressionBuilderCollection.cs
- RegexNode.cs
- AnimationTimeline.cs
- HttpListenerPrefixCollection.cs
- BridgeDataReader.cs
- HMACSHA256.cs
- PromptEventArgs.cs
- ZoneMembershipCondition.cs
- FigureParaClient.cs
- TextTreePropertyUndoUnit.cs
- CompilationRelaxations.cs
- EmptyEnumerable.cs
- Bidi.cs
- MsmqIntegrationBindingElement.cs
- WmlValidationSummaryAdapter.cs
- UserMapPath.cs
- ProfileBuildProvider.cs
- SchemaExporter.cs
- MessageQueueTransaction.cs
- HttpServerVarsCollection.cs
- CacheOutputQuery.cs
- NullRuntimeConfig.cs
- RC2.cs
- InternalRelationshipCollection.cs
- RotateTransform3D.cs
- EditorPart.cs
- QueryPageSettingsEventArgs.cs
- ExcCanonicalXml.cs
- InstanceDataCollection.cs
- PassportAuthenticationModule.cs
- XmlBaseWriter.cs
- CodeEntryPointMethod.cs
- DiscreteKeyFrames.cs
- HttpWriter.cs
- FontEmbeddingManager.cs
- XmlSerializerNamespaces.cs
- WebService.cs
- MemberProjectedSlot.cs
- LiteralControl.cs
- mediaeventargs.cs
- WebPartHelpVerb.cs
- ObjectQuery.cs
- BrowserCapabilitiesFactory.cs
- PropertyTab.cs
- GenericPrincipal.cs
- AttributeSetAction.cs
- AspProxy.cs
- HwndProxyElementProvider.cs