private PooledStream Create(CreateConnectionDelegate createConnectionCallback) { PooledStream stream = null; try { stream = createConnectionCallback(this); if (stream == null) { throw new InternalException(); } if (!stream.CanBePooled) { throw new InternalException(); } stream.PrePush(null); lock (this.m_ObjectList.SyncRoot) { this.m_ObjectList.Add(stream); this.m_TotalObjects = this.m_ObjectList.Count; } } catch (Exception exception) { stream = null; this.m_ResError = exception; this.Abort(); } return(stream); }
/// <devdoc> /// <para>Creates a new PooledStream is allowable</para> /// </devdoc> private PooledStream UserCreateRequest() { // called by user when they were not able to obtain a free object but // instead obtained creation mutex GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::UserCreateRequest"); PooledStream pooledStream = null; if (!ErrorOccurred) { 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()) { pooledStream = Create(m_CreateConnectionCallback); } } } GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::UserCreateRequest", ValidationHelper.HashString(pooledStream)); return(pooledStream); }
internal PooledStream GetConnection(object owningObject, GeneralAsyncDelegate asyncCallback, int creationTimeout) { int num; PooledStream pooledStream = null; bool continueLoop = true; bool flag2 = asyncCallback != null; if (this.m_State != State.Running) { throw new InternalException(); } Interlocked.Increment(ref this.m_WaitCount); WaitHandle[] waitHandles = this.m_WaitHandles; if (!flag2) { while ((pooledStream == null) && continueLoop) { num = WaitHandle.WaitAny(waitHandles, creationTimeout, false); pooledStream = this.Get(owningObject, num, ref continueLoop, ref waitHandles); } } else { num = WaitHandle.WaitAny(waitHandles, 0, false); if (num != 0x102) { pooledStream = this.Get(owningObject, num, ref continueLoop, ref waitHandles); } if (pooledStream == null) { AsyncConnectionPoolRequest asyncRequest = new AsyncConnectionPoolRequest(this, owningObject, asyncCallback, creationTimeout); this.QueueRequest(asyncRequest); } } if (pooledStream != null) { if (!pooledStream.IsInitalizing) { asyncCallback = null; } try { if (!pooledStream.Activate(owningObject, asyncCallback)) { pooledStream = null; } return(pooledStream); } catch { this.PutConnection(pooledStream, owningObject, creationTimeout, false); throw; } } if (!flag2) { throw new InternalException(); } return(pooledStream); }
private void CleanupCallback() { while (this.Count > this.MinPoolSize) { if (this.Semaphore.WaitOne(0, false)) { PooledStream pooledStream = (PooledStream)this.m_StackOld.Pop(); if (pooledStream != null) { this.Destroy(pooledStream); continue; } this.Semaphore.ReleaseSemaphore(); } break; } if (this.Semaphore.WaitOne(0, false)) { while (true) { PooledStream stream2 = (PooledStream)this.m_StackNew.Pop(); if (stream2 == null) { break; } this.m_StackOld.Push(stream2); } this.Semaphore.ReleaseSemaphore(); } }
/// <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); } }
internal ConnectSocketState(ServicePoint servicePoint, PooledStream pooledStream, object owner, Socket s4, Socket s6) { this.servicePoint = servicePoint; this.pooledStream = pooledStream; this.owner = owner; this.s4 = s4; this.s6 = s6; }
private PooledStream Get(object owningObject, int result, ref bool continueLoop, ref WaitHandle[] waitHandles) { PooledStream fromPool = null; int num2 = result; switch (num2) { case 1: { int num = Interlocked.Decrement(ref this.m_WaitCount); continueLoop = false; Exception resError = this.m_ResError; if (num == 0) { this.CancelErrorCallback(); } throw resError; } case 2: try { continueLoop = true; fromPool = this.UserCreateRequest(); if (fromPool != null) { fromPool.PostPop(owningObject); Interlocked.Decrement(ref this.m_WaitCount); continueLoop = false; return(fromPool); } if (((this.Count >= this.MaxPoolSize) && (this.MaxPoolSize != 0)) && !this.ReclaimEmancipatedObjects()) { waitHandles = new WaitHandle[] { this.m_WaitHandles[0], this.m_WaitHandles[1] }; } return(fromPool); } finally { this.CreationMutex.ReleaseMutex(); } break; default: if (num2 == 0x102) { Interlocked.Decrement(ref this.m_WaitCount); continueLoop = false; throw new WebException(NetRes.GetWebStatusString("net_timeout", WebExceptionStatus.ConnectFailure), WebExceptionStatus.Timeout); } break; } Interlocked.Decrement(ref this.m_WaitCount); fromPool = this.GetFromPool(owningObject); continueLoop = false; return(fromPool); }
/// <devdoc> /// <para>Places a new/reusable stream in the new stack of the pool</para> /// </devdoc> private void PutNew(PooledStream pooledStream) { GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew", "#" + ValidationHelper.HashString(pooledStream)); GlobalLog.Assert(null != pooledStream, "Why are we adding a null object to the pool?"); GlobalLog.Assert(pooledStream.CanBePooled, "Non-poolable object in pool."); m_StackNew.Push(pooledStream); Semaphore.ReleaseSemaphore(); GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew"); }
/// <devdoc> /// <para>Places a new/reusable stream in the new stack of the pool</para> /// </devdoc> private void PutNew(PooledStream pooledStream) { GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew", "#" + ValidationHelper.HashString(pooledStream)); GlobalLog.Assert(null != pooledStream, "Why are we adding a null object to the pool?"); GlobalLog.Assert(pooledStream.CanBePooled, "Non-poolable object in pool."); m_StackNew.Push(pooledStream); // ensure that the semaphore's count is incremented to signal an available connection is in // the pool Semaphore.ReleaseSemaphore(); GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew"); }
internal void PutConnection(PooledStream pooledStream, object owningObject, int creationTimeout, bool canReuse) { if (pooledStream == null) { throw new ArgumentNullException("pooledStream"); } pooledStream.PrePush(owningObject); if (this.m_State != State.ShuttingDown) { pooledStream.Deactivate(); if (this.m_WaitCount == 0) { this.CancelErrorCallback(); } if (canReuse && pooledStream.CanBePooled) { this.PutNew(pooledStream); } else { this.Destroy(pooledStream); if (this.m_WaitCount > 0) { if (!this.CreationMutex.WaitOne(creationTimeout, false)) { this.Abort(); } else { try { pooledStream = this.UserCreateRequest(); if (pooledStream != null) { this.PutNew(pooledStream); } } finally { this.CreationMutex.ReleaseMutex(); } } } } } else { this.Destroy(pooledStream); } }
private PooledStream UserCreateRequest() { PooledStream stream = null; if (this.ErrorOccurred || ((this.Count >= this.MaxPoolSize) && (this.MaxPoolSize != 0))) { return(stream); } if (((this.Count & 1) != 1) && this.ReclaimEmancipatedObjects()) { return(stream); } return(this.Create(this.m_CreateConnectionCallback)); }
/// <summary> /// <para>Processes async queued requests that are blocked on needing a free pooled stream /// works as follows: /// 1. while there are blocked requests, take one out of the queue /// 2. Wait for a free connection, when one becomes avail, then notify the request that its there /// 3. repeat 1 until there are no more queued requests /// 4. if there are no more requests waiting to for a free stream, then close down this thread ///</para> /// </summary> private void AsyncThread() { do { while (m_QueuedRequests.Count > 0) { bool continueLoop = true; AsyncConnectionPoolRequest asyncState = null; lock (m_QueuedRequests) { asyncState = (AsyncConnectionPoolRequest)m_QueuedRequests.Dequeue(); } WaitHandle [] localWaitHandles = m_WaitHandles; PooledStream PooledStream = null; try { while ((PooledStream == null) && continueLoop) { int result = WaitHandle.WaitAny(localWaitHandles, asyncState.CreationTimeout, false); PooledStream = Get(asyncState.OwningObject, result, ref continueLoop, ref localWaitHandles); } PooledStream.Activate(asyncState.OwningObject, asyncState.AsyncCallback); } catch (Exception e) { if (PooledStream != null) { PooledStream.Close(); PutConnection(PooledStream, asyncState.OwningObject, asyncState.CreationTimeout); } asyncState.AsyncCallback(asyncState.OwningObject, e); } catch { if (PooledStream != null) { PooledStream.Close(); PutConnection(PooledStream, asyncState.OwningObject, asyncState.CreationTimeout); } asyncState.AsyncCallback(asyncState.OwningObject, new Exception(SR.GetString(SR.net_nonClsCompliantException))); } } Thread.Sleep(500); lock (m_QueuedRequests) { if (m_QueuedRequests.Count == 0) { m_AsyncThread = null; break; } } } while (true); }
private PooledStream GetFromPool(object owningObject) { PooledStream stream = null; stream = (PooledStream)this.m_StackNew.Pop(); if (stream == null) { stream = (PooledStream)this.m_StackOld.Pop(); } if (stream != null) { stream.PostPop(owningObject); } return(stream); }
private void AsyncThread() { Label_00B1: while (this.m_QueuedRequests.Count > 0) { bool continueLoop = true; AsyncConnectionPoolRequest request = null; lock (this.m_QueuedRequests) { request = (AsyncConnectionPoolRequest)this.m_QueuedRequests.Dequeue(); } WaitHandle[] waitHandles = this.m_WaitHandles; PooledStream pooledStream = null; try { while ((pooledStream == null) && continueLoop) { int result = WaitHandle.WaitAny(waitHandles, request.CreationTimeout, false); pooledStream = this.Get(request.OwningObject, result, ref continueLoop, ref waitHandles); } pooledStream.Activate(request.OwningObject, request.AsyncCallback); continue; } catch (Exception exception) { if (pooledStream != null) { this.PutConnection(pooledStream, request.OwningObject, request.CreationTimeout, false); } request.AsyncCallback(request.OwningObject, exception); continue; } } Thread.Sleep(500); lock (this.m_QueuedRequests) { if (this.m_QueuedRequests.Count == 0) { this.m_AsyncThread = null; } else { goto Label_00B1; } } }
private void Destroy(PooledStream pooledStream) { if (pooledStream != null) { try { lock (this.m_ObjectList.SyncRoot) { this.m_ObjectList.Remove(pooledStream); this.m_TotalObjects = this.m_ObjectList.Count; } } finally { pooledStream.Dispose(); } } }
/// <devdoc> /// <para>Reclaim any pooled Streams that have seen their users/WebRequests GCed away</para> /// </devdoc> private bool ReclaimEmancipatedObjects() { bool emancipatedObjectFound = false; GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::ReclaimEmancipatedObjects"); lock (m_ObjectList.SyncRoot) { object[] objectList = m_ObjectList.ToArray(); if (null != objectList) { for (int i = 0; i < objectList.Length; ++i) { PooledStream pooledStream = (PooledStream)objectList[i]; if (null != pooledStream) { bool locked = false; try { locked = Monitor.TryEnter(pooledStream); if (locked) { if (pooledStream.IsEmancipated) { GlobalLog.Print("EmancipatedObject pooledStream#" + ValidationHelper.HashString(pooledStream)); PutConnection(pooledStream, null, Timeout.Infinite); emancipatedObjectFound = true; } } } finally { if (locked) { Monitor.Exit(pooledStream); } } } } } } GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::ReclaimEmancipatedObjects", emancipatedObjectFound.ToString()); return(emancipatedObjectFound); }
/// <summary> /// <para>Creates a new PooledStream, performs checks as well on the new stream</para> /// </summary> private PooledStream Create(CreateConnectionDelegate createConnectionCallback) { GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::Create"); PooledStream newObj = null; try { newObj = createConnectionCallback(this); if (null == newObj) { throw new InternalException(); // Create succeeded, but null object } if (!newObj.CanBePooled) { throw new InternalException(); // Create succeeded, but non-poolable object } newObj.PrePush(null); lock (m_ObjectList.SyncRoot) { m_ObjectList.Add(newObj); m_TotalObjects = m_ObjectList.Count; } GlobalLog.Print("Create pooledStream#" + ValidationHelper.HashString(newObj)); } catch (Exception e) { GlobalLog.Print("Pool Exception: " + e.Message); newObj = null; // set to null, so we do not return bad new object // Failed to create instance m_ResError = e; Abort(); } catch { GlobalLog.Print("Pool Exception: Non-CLS Compliant Exception"); newObj = null; // set to null, so we do not return bad new object // Failed to create instance m_ResError = new Exception(SR.GetString(SR.net_nonClsCompliantException)); Abort(); } GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::Create", ValidationHelper.HashString(newObj)); return(newObj); }
/// <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>Destroys a pooled stream from the pool</para> /// </summary> private void Destroy(PooledStream pooledStream) { GlobalLog.Print("Destroy pooledStream#" + ValidationHelper.HashString(pooledStream)); try { lock (m_ObjectList.SyncRoot) { m_ObjectList.Remove(pooledStream); m_TotalObjects = m_ObjectList.Count; } } finally { if (null != pooledStream) { pooledStream.Destroy(); } } }
internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6, int timeout) { Socket socket = null; Socket socket2 = null; Socket socket3 = null; Exception exception = null; WebExceptionStatus connectFailure = WebExceptionStatus.ConnectFailure; address = null; if (Socket.OSSupportsIPv4) { socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } if (Socket.OSSupportsIPv6) { socket2 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); } abortSocket = socket; abortSocket6 = socket2; ConnectSocketState state = null; if (async) { state = new ConnectSocketState(this, PooledStream, owner, socket, socket2); } connectFailure = this.ConnectSocket(socket, socket2, ref socket3, ref address, state, timeout, out exception); if (connectFailure == WebExceptionStatus.Pending) { return(null); } if (connectFailure != WebExceptionStatus.Success) { throw new WebException(NetRes.GetWebStatusString(connectFailure), ((connectFailure == WebExceptionStatus.ProxyNameResolutionFailure) || (connectFailure == WebExceptionStatus.NameResolutionFailure)) ? this.Host : null, exception, connectFailure, null, WebExceptionInternalStatus.ServicePointFatal); } if (socket3 == null) { throw new IOException(SR.GetString("net_io_transportfailure")); } this.CompleteGetConnection(socket, socket2, socket3, address); return(socket3); }
private bool ReclaimEmancipatedObjects() { bool flag = false; lock (this.m_ObjectList.SyncRoot) { object[] objArray = this.m_ObjectList.ToArray(); if (objArray == null) { return(flag); } for (int i = 0; i < objArray.Length; i++) { PooledStream stream = (PooledStream)objArray[i]; if (stream != null) { bool lockTaken = false; try { Monitor.TryEnter(stream, ref lockTaken); if (lockTaken && stream.IsEmancipated) { this.PutConnection(stream, null, -1); flag = true; } } finally { if (lockTaken) { Monitor.Exit(stream); } } } } } return(flag); }
/// <summary> /// <para>Retrieves a pooled stream from the pool proper /// this work by first attemting to find something in the pool on the New stack /// and then trying the Old stack if something is not there availble </para> /// </summary> private PooledStream GetFromPool(object owningObject) { PooledStream res = null; GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::GetFromPool"); res = (PooledStream)m_StackNew.Pop(); if (null == res) { res = (PooledStream)m_StackOld.Pop(); } // Shouldn't be null, we could assert here. GlobalLog.Assert(res != null, "GetFromPool called with nothing in the pool!"); if (null != res) { res.PostPop(owningObject); GlobalLog.Print("GetFromGeneralPool pooledStream#" + ValidationHelper.HashString(res)); } GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::GetFromPool", ValidationHelper.HashString(res)); return(res); }
/// <summary> /// <para>Retrieves a pooled stream from the pool proper /// this work by first attemting to find something in the pool on the New stack /// and then trying the Old stack if something is not there availble </para> /// </summary> private PooledStream GetFromPool(object owningObject) { PooledStream res = null; GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::GetFromPool"); res = (PooledStream)m_StackNew.Pop(); if (null == res) { res = (PooledStream)m_StackOld.Pop(); } // The semaphore guaranteed that a connection was available so if res is // null it means that this contract has been violated somewhere GlobalLog.Assert(res != null, "GetFromPool called with nothing in the pool!"); if (null != res) { res.PostPop(owningObject); GlobalLog.Print("GetFromGeneralPool pooledStream#" + ValidationHelper.HashString(res)); } GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::GetFromPool", ValidationHelper.HashString(res)); return(res); }
internal void ForceCleanup() { if (Logging.On) { Logging.Enter(Logging.Web, "ConnectionPool::ForceCleanup"); } while (this.Count > 0) { if (!this.Semaphore.WaitOne(0, false)) { break; } PooledStream pooledStream = (PooledStream)this.m_StackNew.Pop(); if (pooledStream == null) { pooledStream = (PooledStream)this.m_StackOld.Pop(); } this.Destroy(pooledStream); } if (Logging.On) { Logging.Exit(Logging.Web, "ConnectionPool::ForceCleanup"); } }
private void PutNew(PooledStream pooledStream) { this.m_StackNew.Push(pooledStream); this.Semaphore.ReleaseSemaphore(); }
/// <devdoc> /// <para>Attempts to return a PooledStream to the pool</para> /// </devdoc> internal void PutConnection(PooledStream pooledStream, object owningObject, int creationTimeout) { 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 (pooledStream.CanBePooled) { PutNew(pooledStream); } else { Destroy(pooledStream); // 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); } }
internal void PutConnection(PooledStream pooledStream, object owningObject, int creationTimeout) { this.PutConnection(pooledStream, owningObject, creationTimeout, true); }
internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6, int timeout) { Socket socket = null; Socket socket2 = null; Socket socket3 = null; Exception exception = null; WebExceptionStatus connectFailure = WebExceptionStatus.ConnectFailure; address = null; if (Socket.OSSupportsIPv4) { socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } if (Socket.OSSupportsIPv6) { socket2 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); } abortSocket = socket; abortSocket6 = socket2; ConnectSocketState state = null; if (async) { state = new ConnectSocketState(this, PooledStream, owner, socket, socket2); } connectFailure = this.ConnectSocket(socket, socket2, ref socket3, ref address, state, timeout, out exception); if (connectFailure == WebExceptionStatus.Pending) { return null; } if (connectFailure != WebExceptionStatus.Success) { throw new WebException(NetRes.GetWebStatusString(connectFailure), ((connectFailure == WebExceptionStatus.ProxyNameResolutionFailure) || (connectFailure == WebExceptionStatus.NameResolutionFailure)) ? this.Host : null, exception, connectFailure, null, WebExceptionInternalStatus.ServicePointFatal); } if (socket3 == null) { throw new IOException(SR.GetString("net_io_transportfailure")); } this.CompleteGetConnection(socket, socket2, socket3, address); return socket3; }
/// <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(); } }
internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6) { throw new NotImplementedException(); }
/// <devdoc> /// <para>Attempts to create a PooledStream, by trying to get a pooled Connection, /// or by creating its own new one</para> /// </devdoc> internal PooledStream GetConnection(object owningObject, GeneralAsyncDelegate asyncCallback, int creationTimeout) { int result; PooledStream stream = null; bool continueLoop = true; bool async = (asyncCallback != null) ? true : false; GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::GetConnection"); if (m_State != State.Running) { throw new InternalException(); } Interlocked.Increment(ref m_WaitCount); WaitHandle[] localWaitHandles = m_WaitHandles; if (async) { result = WaitHandle.WaitAny(localWaitHandles, 0, false); if (result != WaitTimeout) { stream = Get(owningObject, result, ref continueLoop, ref localWaitHandles); } if (stream == null) { GlobalLog.Print("GetConnection:" + ValidationHelper.HashString(this) + " going async"); AsyncConnectionPoolRequest asyncState = new AsyncConnectionPoolRequest(this, owningObject, asyncCallback, creationTimeout); QueueRequest(asyncState); } } else { // loop while we don't have an error/timeout and we haven't gotten a stream yet while ((stream == null) && continueLoop) { result = WaitHandle.WaitAny(localWaitHandles, creationTimeout, false); stream = Get(owningObject, result, ref continueLoop, ref localWaitHandles); } } if (null != stream) { // if there is already a stream, then we're not going async if (!stream.IsInitalizing) { asyncCallback = null; } try{ // If activate returns false, it is going to finish asynchronously // and therefore the stream will be returned in a callback and // we should not return it here (return null) if (stream.Activate(owningObject, asyncCallback) == false) { stream = null; } } catch { stream.Close(); PutConnection(stream, owningObject, creationTimeout); throw; } } else if (!async) { throw new InternalException(); } GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::GetConnection", ValidationHelper.HashString(stream)); return(stream); }
/// <devdoc> /// <para> /// Attempts to return a PooledStream to the pool. Default is that it can be reused if it can /// also be pooled. /// </para> /// </devdoc> internal void PutConnection(PooledStream pooledStream, object owningObject, int creationTimeout) { // ok to reuse PutConnection(pooledStream, owningObject, creationTimeout, true); }
/// <devdoc> /// <para>Places a new/reusable stream in the new stack of the pool</para> /// </devdoc> private void PutNew(PooledStream pooledStream) { GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew", "#"+ValidationHelper.HashString(pooledStream)); GlobalLog.Assert(null != pooledStream, "Why are we adding a null object to the pool?"); GlobalLog.Assert(pooledStream.CanBePooled, "Non-poolable object in pool."); m_StackNew.Push(pooledStream); // ensure that the semaphore's count is incremented to signal an available connection is in // the pool Semaphore.ReleaseSemaphore(); GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew"); }
internal void GetConnection(ServicePoint servicePoint) { if (this.isConnected) { throw new InvalidOperationException(SR.GetString("SmtpAlreadyConnected")); } if (Logging.On) { Logging.Associate(Logging.Web, this, servicePoint); } this.connectionPool = ConnectionPoolManager.GetConnectionPool(servicePoint, "", m_CreateConnectionCallback); PooledStream pooledStream = this.connectionPool.GetConnection(this, null, this.Timeout); while ((((SmtpPooledStream) pooledStream).creds != null) && (((SmtpPooledStream) pooledStream).creds != this.credentials)) { this.connectionPool.PutConnection(pooledStream, pooledStream.Owner, this.Timeout, false); pooledStream = this.connectionPool.GetConnection(this, null, this.Timeout); } if (Logging.On) { Logging.Associate(Logging.Web, this, pooledStream); } lock (this) { this.pooledStream = pooledStream; } ((SmtpPooledStream) pooledStream).creds = this.credentials; this.responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); pooledStream.UpdateLifetime(); if (((SmtpPooledStream) pooledStream).previouslyUsed) { this.isConnected = true; } else { LineInfo info = this.responseReader.GetNextReplyReader().ReadLine(); if (info.StatusCode != SmtpStatusCode.ServiceReady) { throw new SmtpException(info.StatusCode, info.Line, true); } try { this.extensions = EHelloCommand.Send(this, this.client.clientDomain); this.ParseExtensions(this.extensions); } catch (SmtpException exception) { if ((exception.StatusCode != SmtpStatusCode.CommandUnrecognized) && (exception.StatusCode != SmtpStatusCode.CommandNotImplemented)) { throw exception; } HelloCommand.Send(this, this.client.clientDomain); this.supportedAuth = SupportedAuth.Login; } if (this.enableSsl) { if (!this.serverSupportsStartTls && !(pooledStream.NetworkStream is TlsStream)) { throw new SmtpException(SR.GetString("MailServerDoesNotSupportStartTls")); } StartTlsCommand.Send(this); TlsStream stream2 = new TlsStream(servicePoint.Host, pooledStream.NetworkStream, this.clientCertificates, servicePoint, this.client, null); pooledStream.NetworkStream = stream2; this.channelBindingToken = stream2.GetChannelBinding(ChannelBindingKind.Unique); this.responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); this.extensions = EHelloCommand.Send(this, this.client.clientDomain); this.ParseExtensions(this.extensions); } if (this.credentials != null) { for (int i = 0; i < this.authenticationModules.Length; i++) { Authorization authorization; if (this.AuthSupported(this.authenticationModules[i])) { NetworkCredential credential = this.credentials.GetCredential(servicePoint.Host, servicePoint.Port, this.authenticationModules[i].AuthenticationType); if (credential != null) { authorization = this.SetContextAndTryAuthenticate(this.authenticationModules[i], credential, null); if ((authorization != null) && (authorization.Message != null)) { info = AuthCommand.Send(this, this.authenticationModules[i].AuthenticationType, authorization.Message); if (info.StatusCode != SmtpStatusCode.CommandParameterNotImplemented) { goto Label_0363; } } } } continue; Label_02F2: authorization = this.authenticationModules[i].Authenticate(info.Line, null, this, this.client.TargetName, this.channelBindingToken); if (authorization == null) { throw new SmtpException(SR.GetString("SmtpAuthenticationFailed")); } info = AuthCommand.Send(this, authorization.Message); if (info.StatusCode == ((SmtpStatusCode) 0xeb)) { this.authenticationModules[i].CloseContext(this); this.isConnected = true; return; } Label_0363: if (info.StatusCode == ((SmtpStatusCode) 0x14e)) { goto Label_02F2; } } } this.isConnected = true; } }
/// <devdoc> /// <para>Places a new/reusable stream in the new stack of the pool</para> /// </devdoc> private void PutNew(PooledStream pooledStream) { GlobalLog.Enter("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew", "#"+ValidationHelper.HashString(pooledStream)); GlobalLog.Assert(null != pooledStream, "Why are we adding a null object to the pool?"); GlobalLog.Assert(pooledStream.CanBePooled, "Non-poolable object in pool."); m_StackNew.Push(pooledStream); Semaphore.ReleaseSemaphore(); GlobalLog.Leave("ConnectionPool#" + ValidationHelper.HashString(this) + "::PutNew"); }
/// <devdoc> /// <para> /// Tempory for getting a new Connection for FTP client, for the time being /// </para> /// </devdoc> internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6) { Socket socket = null; Socket socket6 = null; Socket finalSocket = null; Exception innerException = null; WebExceptionStatus ws = WebExceptionStatus.ConnectFailure; address = null; // // if we will not create a tunnel through a proxy then create // and connect the socket we will use for the connection // // // IPv6 Support: If IPv6 is enabled, then we create a second socket that ServicePoint // will use if it wants to connect via IPv6. // if ( Socket.OSSupportsIPv4 ) { socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); } if ( Socket.OSSupportsIPv6 ) { socket6 = new Socket(AddressFamily.InterNetworkV6,SocketType.Stream,ProtocolType.Tcp); } abortSocket = socket; abortSocket6 = socket6; // // Setup socket timeouts for [....] requests // // ConnectSocketState state = null; if (async) { state = new ConnectSocketState(this, PooledStream, owner, socket, socket6); } ws = ConnectSocket(socket, socket6, ref finalSocket, ref address, state, out innerException); if (ws == WebExceptionStatus.Pending) { return null; } if (ws != WebExceptionStatus.Success) { throw new WebException( NetRes.GetWebStatusString(ws), ws == WebExceptionStatus.ProxyNameResolutionFailure || ws == WebExceptionStatus.NameResolutionFailure ? Host : null, innerException, ws, null, /* no response */ WebExceptionInternalStatus.ServicePointFatal); } // // There should be no means for socket to be null at this // point, but the damage is greater if we just carry on // without ensuring that it's good. // if ( finalSocket == null ) { throw new IOException(SR.GetString(SR.net_io_transportfailure)); } CompleteGetConnection(socket, socket6, finalSocket, address); return finalSocket; }
/// <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); }
internal void GetConnection(ServicePoint servicePoint) { if (isConnected) { throw new InvalidOperationException(SR.GetString(SR.SmtpAlreadyConnected)); } if (Logging.On) Logging.Associate(Logging.Web, this, servicePoint); Debug.Assert(servicePoint != null, "servicePoint was null from SmtpTransport"); connectionPool = ConnectionPoolManager.GetConnectionPool(servicePoint, "", m_CreateConnectionCallback); PooledStream pooledStream = connectionPool.GetConnection((object)this, null, Timeout); while (((SmtpPooledStream)pooledStream).creds != null && ((SmtpPooledStream)pooledStream).creds != credentials) { // destroy this connection so that a new connection can be created // in order to use the proper credentials. Do not just close the // connection since it's in a state where a QUIT could be sent connectionPool.PutConnection(pooledStream, pooledStream.Owner, Timeout, false); pooledStream = connectionPool.GetConnection((object)this, null, Timeout); } if (Logging.On) Logging.Associate(Logging.Web, this, pooledStream); lock (this) { this.pooledStream = pooledStream; } ((SmtpPooledStream)pooledStream).creds = credentials; responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); //set connectionlease pooledStream.UpdateLifetime(); //if the stream was already used, then we've already done the handshake if (((SmtpPooledStream)pooledStream).previouslyUsed == true) { isConnected = true; return; } LineInfo info = responseReader.GetNextReplyReader().ReadLine(); switch (info.StatusCode) { case SmtpStatusCode.ServiceReady: { break; } default: { throw new SmtpException(info.StatusCode, info.Line, true); } } try { extensions = EHelloCommand.Send(this, client.clientDomain); ParseExtensions(extensions); } catch (SmtpException e) { if ((e.StatusCode != SmtpStatusCode.CommandUnrecognized) && (e.StatusCode != SmtpStatusCode.CommandNotImplemented)) { throw e; } HelloCommand.Send(this, client.clientDomain); //if ehello isn't supported, assume basic login supportedAuth = SupportedAuth.Login; } #if !FEATURE_PAL // Establish TLS if (enableSsl) { if (!serverSupportsStartTls) { // Either TLS is already established or server does not support TLS if (!(pooledStream.NetworkStream is TlsStream)) { throw new SmtpException(SR.GetString(SR.MailServerDoesNotSupportStartTls)); } } StartTlsCommand.Send(this); TlsStream TlsStream = new TlsStream(servicePoint.Host, pooledStream.NetworkStream, clientCertificates, servicePoint, client, null); pooledStream.NetworkStream = TlsStream; //for SMTP, the CBT should be unique this.channelBindingToken = TlsStream.GetChannelBinding(ChannelBindingKind.Unique); responseReader = new SmtpReplyReaderFactory(pooledStream.NetworkStream); // According to RFC 3207: The client SHOULD send an EHLO command // as the first command after a successful TLS negotiation. extensions = EHelloCommand.Send(this, client.clientDomain); ParseExtensions(extensions); } #endif // !FEATURE_PAL //if no credentials were supplied, try anonymous //servers don't appear to anounce that they support anonymous login. if (credentials != null) { for (int i = 0; i < authenticationModules.Length; i++) { //only authenticate if the auth protocol is supported - [....] if (!AuthSupported(authenticationModules[i])) { continue; } NetworkCredential credential = credentials.GetCredential(servicePoint.Host, servicePoint.Port, authenticationModules[i].AuthenticationType); if (credential == null) continue; Authorization auth = SetContextAndTryAuthenticate(authenticationModules[i], credential, null); if (auth != null && auth.Message != null) { info = AuthCommand.Send(this, authenticationModules[i].AuthenticationType, auth.Message); if (info.StatusCode == SmtpStatusCode.CommandParameterNotImplemented) { continue; } while ((int)info.StatusCode == 334) { auth = authenticationModules[i].Authenticate(info.Line, null, this, this.client.TargetName, this.channelBindingToken); if (auth == null) { throw new SmtpException(SR.GetString(SR.SmtpAuthenticationFailed)); } info = AuthCommand.Send(this, auth.Message); if ((int)info.StatusCode == 235) { authenticationModules[i].CloseContext(this); isConnected = true; return; } } } } } isConnected = true; }
internal Socket GetConnection(PooledStream PooledStream, object owner, bool async, out IPAddress address, ref Socket abortSocket, ref Socket abortSocket6) { throw new NotImplementedException (); }
/// <summary> /// <para>Destroys a pooled stream from the pool</para> /// </summary> private void Destroy(PooledStream pooledStream) { GlobalLog.Print("Destroy pooledStream#"+ValidationHelper.HashString(pooledStream)); try { lock (m_ObjectList.SyncRoot) { m_ObjectList.Remove(pooledStream); m_TotalObjects = m_ObjectList.Count; } } finally { if (null != pooledStream) { pooledStream.Destroy(); } } }