/// <summary> /// Cleans up everything in both the old and new stack. If a connection is in use /// then it will be on neither stack and it is the responsibility of the object /// using that connection to clean it up when it is finished using it. This does /// not clean up the ConnectionPool object and new connections can still be /// created if needed in the future should this ConnectionPool object be reused /// /// preconditions: none /// /// postconditions: any connections not currently in use by an object will be /// gracefully terminated and purged from this connection pool /// </summary> internal void ForceCleanup() { if (Logging.On) { Logging.Enter(Logging.Web, "ConnectionPool::ForceCleanup"); } // If WaitOne returns false, all connections in the pool are in use // so no cleanup should be performed. The last object owning // a connection from the pool will perform final cleanup. while (Count > 0) { if (Semaphore.WaitOne(0, false)) { // Try to clean up from new stack first, if there isn't anything on new // then try old. When we lock the Semaphore, it gives us a license to // remove only one connection from the pool but it can be from either // stack since if the Semaphore is locked by another thread it means that // there must have been more than one connection available in either stack PooledStream pooledStream = (PooledStream)m_StackNew.Pop(); // no streams in stack new, there must therefore be one in stack old since we // were able to acquire the semaphore if (pooledStream == null) { pooledStream = (PooledStream)m_StackOld.Pop(); } Debug.Assert(pooledStream != null, "Acquired Semaphore with no connections in either stack"); Destroy(pooledStream); } else { // couldn't get semaphore, nothing to do here break; } } if (Logging.On) { Logging.Exit(Logging.Web, "ConnectionPool::ForceCleanup"); } }
/// <summary> /// <para>This is called by a timer, to check for needed cleanup of idle pooled streams</para> /// </summary> private void CleanupCallback() { // Called when the cleanup-timer ticks over. // // This is the automatic prunning method. Every period, we will perform a two-step // process. First, for the objects above MinPool, we will obtain the semaphore for // the object and then destroy it if it was on the old stack. We will continue this // until we either reach MinPool size, or we are unable to obtain a free object, or // until we have exhausted all the objects on the old stack. After that, push all // 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, a object is prunned if unused for at least one period but not // more than two periods. // Destroy free objects above MinPool size from old stack. while (Count > MinPoolSize) // While above MinPoolSize... { if (Semaphore.WaitOne(0, false)) // != WaitTimeout // We obtained a objects from the semaphore. { PooledStream pooledStream = (PooledStream)m_StackOld.Pop(); if (null != pooledStream) { // If we obtained one from the old stack, destroy it. Destroy(pooledStream); } else { // Else we exhausted the old stack, so break. Semaphore.ReleaseSemaphore(); break; } } else { break; } } // Push to the old-stack. For each free object, move object from new stack // to old stack. if (Semaphore.WaitOne(0, false)) // != WaitTimeout { for (;;) { PooledStream pooledStream = (PooledStream)m_StackNew.Pop(); if (null == pooledStream) { break; } GlobalLog.Assert(!pooledStream.IsEmancipated, "Pooled object not in pool."); GlobalLog.Assert(pooledStream.CanBePooled, "Pooled object is not poolable."); m_StackOld.Push(pooledStream); } Semaphore.ReleaseSemaphore(); } }