/// <devdoc> /// <para> /// Attempts to return a PooledStream to the pool. If canReuse is false, then the /// connection will be destroyed even if it is marked as reusable and a new conneciton will /// be created. If it is true, then the connection will still be checked to ensure that /// it can be pooled and will be cleaned up if it can not for another reason. /// </para> /// </devdoc> internal void PutConnection(PooledStream pooledStream, object owningObject, int creationTimeout, bool canReuse) { GlobalLog.Print("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutConnection"); if (pooledStream == null) { throw new ArgumentNullException("pooledStream"); } pooledStream.PrePush(owningObject); if (m_State != State.ShuttingDown) { pooledStream.Deactivate(); // cancel our error status, if we have no new requests waiting anymore if (m_WaitCount == 0) { CancelErrorCallback(); } if (canReuse && pooledStream.CanBePooled) { PutNew(pooledStream); } else { try { Destroy(pooledStream); } finally { // Make sure to release the mutex even under error conditions. // Make sure we recreate a new pooled stream, if there are requests for a stream // at this point if (m_WaitCount > 0) { if (!CreationMutex.WaitOne(creationTimeout, false)) { Abort(); } else { try { pooledStream = UserCreateRequest(); if (null != pooledStream) { PutNew(pooledStream); } } finally { CreationMutex.ReleaseMutex(); } } } } } } else { // If we're shutting down, we destroy the object. Destroy(pooledStream); } }
/// <summary> /// <para>Retrieves the pooled stream out of the pool, does this by using the result /// of a WaitAny as input, and then based on whether it has a mutex, event, semaphore, /// or timeout decides what action to take</para> /// </summary> private PooledStream Get(object owningObject, int result, ref bool continueLoop, ref WaitHandle [] waitHandles) { PooledStream pooledStream = null; GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::Get", result.ToString()); // 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 (result) { case WaitTimeout: Interlocked.Decrement(ref m_WaitCount); continueLoop = false; GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::Get", "throw Timeout WebException"); throw new WebException(NetRes.GetWebStatusString("net_timeout", WebExceptionStatus.ConnectFailure), WebExceptionStatus.Timeout); case ErrorHandleIndex: // Throw the error that PoolCreateRequest stashed. int newWaitCount = Interlocked.Decrement(ref m_WaitCount); continueLoop = false; Exception exceptionToThrow = m_ResError; if (newWaitCount == 0) { CancelErrorCallback(); } throw exceptionToThrow; case CreationHandleIndex: try { continueLoop = true; pooledStream = UserCreateRequest(); if (null != pooledStream) { pooledStream.PostPop(owningObject); Interlocked.Decrement(ref m_WaitCount); continueLoop = false; } else { // 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 (!ReclaimEmancipatedObjects()) { // modify handle array not to wait on creation mutex anymore waitHandles = new WaitHandle[2]; waitHandles[0] = m_WaitHandles[0]; waitHandles[1] = m_WaitHandles[1]; } } } } finally { CreationMutex.ReleaseMutex(); } break; default: // // guaranteed available inventory // Interlocked.Decrement(ref m_WaitCount); pooledStream = GetFromPool(owningObject); continueLoop = false; break; } GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::Get", ValidationHelper.HashString(pooledStream)); return(pooledStream); }