// Author:
// Lluis Sanchez Gual (lluis@ximian.com)
// Christian Hergert (christian.hergert@gmail.com)
+// Gonzalo Paniagua Javier (gonzalo@novell.com)
//
// Copyright (C) 2004 Novell, Inc.
-//
+// Copyright (C) 2009 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
//
using System;
-#if NET_2_0
-using System.Collections.Generic;
-#else
using System.Collections;
-#endif
+using System.Text;
using System.Threading;
namespace Mono.Data.Tds.Protocol
{
public class TdsConnectionPoolManager
{
-#if NET_2_0
- Dictionary <string, TdsConnectionPool> pools = new Dictionary <string, TdsConnectionPool> ();
-#else
- Hashtable pools = new Hashtable ();
-#endif
+ Hashtable pools = Hashtable.Synchronized (new Hashtable ());
TdsVersion version;
public TdsConnectionPoolManager (TdsVersion version)
public TdsConnectionPool GetConnectionPool (string connectionString, TdsConnectionInfo info)
{
- lock (pools)
- {
- TdsConnectionPool pool = null;
-#if NET_2_0
- pools.TryGetValue (connectionString, out pool);
-#else
+ TdsConnectionPool pool = (TdsConnectionPool) pools [connectionString];
+ if (pool == null) {
+ pools [connectionString] = new TdsConnectionPool (this, info);
pool = (TdsConnectionPool) pools [connectionString];
-#endif
- if (pool == null) {
- pool = new TdsConnectionPool (this, info);
- pools [connectionString] = pool;
- }
- return pool;
}
+ return pool;
}
public TdsConnectionPool GetConnectionPool (string connectionString)
{
- TdsConnectionPool pool = null;
-#if NET_2_0
- pools.TryGetValue (connectionString, out pool);
-#else
- pool = (TdsConnectionPool) pools [connectionString];
-#endif
- return pool;
+ return (TdsConnectionPool) pools [connectionString];
}
-#if NET_2_0
- public IDictionary <string, TdsConnectionPool> GetConnectionPool ()
-#else
- public IDictionary GetConnectionPool ()
-#endif
- {
- return pools;
- }
-
public virtual Tds CreateConnection (TdsConnectionInfo info)
{
+ //Console.WriteLine ("CreateConnection: TdsVersion:{0}", version);
switch (version)
{
case TdsVersion.tds42:
}
throw new NotSupportedException ();
}
+
+ public IDictionary GetConnectionPool ()
+ {
+ return pools;
+ }
}
public class TdsConnectionInfo
public int Timeout;
public int PoolMinSize;
public int PoolMaxSize;
+
+ public override string ToString ()
+ {
+ StringBuilder sb = new StringBuilder ();
+ sb.AppendFormat ("DataSouce: {0}\n", DataSource);
+ sb.AppendFormat ("Port: {0}\n", Port);
+ sb.AppendFormat ("PacketSize: {0}\n", PacketSize);
+ sb.AppendFormat ("Timeout: {0}\n", Timeout);
+ sb.AppendFormat ("PoolMinSize: {0}\n", PoolMinSize);
+ sb.AppendFormat ("PoolMaxSize: {0}", PoolMaxSize);
+ return sb.ToString ();
+ }
}
public class TdsConnectionPool
{
-#if NET_2_0
- Tds[] list;
-#else
- object [] list;
-#endif
-
TdsConnectionInfo info;
- bool pooling = true;
+ bool no_pooling;
TdsConnectionPoolManager manager;
- ManualResetEvent connAvailable;
+ Queue available;
+ ArrayList conns;
public TdsConnectionPool (TdsConnectionPoolManager manager, TdsConnectionInfo info)
{
- int n = 0;
-
this.info = info;
this.manager = manager;
-#if NET_2_0
- list = new Tds[info.PoolMaxSize];
-#else
- list = new object [info.PoolMaxSize];
-#endif
-
- // Placeholder for future connections are at the beginning of the array.
- for (; n < info.PoolMaxSize - info.PoolMinSize; n++)
- list [n] = null;
+ conns = new ArrayList (info.PoolMaxSize);
+ available = new Queue (info.PoolMaxSize);
+ InitializePool ();
+ }
- // Pre-populate with minimum number of connections
- for (; n < list.Length; n++) {
- list [n] = CreateConnection ();
+ void InitializePool ()
+ {
+ /* conns.Count might not be 0 when we are resetting the connection pool */
+ for (int i = conns.Count; i < info.PoolMinSize; i++) {
+ try {
+ Tds t = manager.CreateConnection (info);
+ conns.Add (t);
+ available.Enqueue (t);
+ } catch {
+ // Ignore. GetConnection will throw again.
+ }
}
-
- // Event that notifies a connection is available in the pool
- connAvailable = new ManualResetEvent (false);
}
public bool Pooling {
- get { return pooling; }
- set { pooling = value; }
+ get { return !no_pooling; }
+ set { no_pooling = !value; }
}
#region Methods
+ int in_progress;
public Tds GetConnection ()
{
- Tds connection = null;
- int index;
-
- retry:
- // Reset the connection available event
- connAvailable.Reset ();
-
- index = list.Length - 1;
-
- do {
-#if NET_2_0
- connection = list [index];
-#else
- connection = (Tds) list [index];
-#endif
-
- if (connection == null) {
- // Attempt take-over of array position
- connection = CreateConnection ();
- (connection as Tds).poolStatus = 1;
+ if (no_pooling)
+ return manager.CreateConnection (info);
-#if NET_2_0
- if (Interlocked.CompareExchange<Tds> (ref list [index], connection, null) != null) {
-#else
- if (Interlocked.CompareExchange (ref list [index], connection, null) != null) {
-#endif
- // Someone beat us to the punch
- connection = null;
- } else {
- continue;
+ Tds result = null;
+ bool create_new;
+ int retries = info.PoolMaxSize * 2;
+retry:
+ while (result == null) {
+ create_new = false;
+ lock (available) {
+ if (available.Count > 0) {
+ result = (Tds) available.Dequeue ();
+ break; // .. and do the reset out of the loop
}
- } else {
- if (Interlocked.CompareExchange (ref (connection as Tds).poolStatus, 1, 0) != 0) {
- // Someone else owns this connection
- connection = null;
- } else {
- if (!connection.Reset ()) {
- ThreadPool.QueueUserWorkItem (new WaitCallback (DestroyConnection), connection);
- // remove connection from pool
- list [index] = connection = null;
- // allow slot be re-used in same run
+ Monitor.Enter (conns);
+ try {
+ if (conns.Count >= info.PoolMaxSize - in_progress) {
+ Monitor.Exit (conns);
+ bool got_lock = Monitor.Wait (available, info.Timeout * 1000);
+ if (!got_lock) {
+ throw new InvalidOperationException (
+ "Timeout expired. The timeout period elapsed before a " +
+ "connection could be obtained. A possible explanation " +
+ "is that all the connections in the pool are in use, " +
+ "and the maximum pool size is reached.");
+ } else if (available.Count > 0) {
+ result = (Tds) available.Dequeue ();
+ break; // .. and do the reset out of the loop
+ }
continue;
} else {
- continue;
+ create_new = true;
+ in_progress++;
}
+ } finally {
+ Monitor.Exit (conns); // Exiting if not owned is ok < 2.x
}
}
-
- index--;
-
- if (index < 0) {
- // TODO: Maintain a list of indices of released connection to save some loop over
- // Honor timeout - if pool is full, and no connections are available within the
- // timeout period - just throw the exception
- if (info.Timeout > 0
- && !connAvailable.WaitOne (new TimeSpan (0, 0, info.Timeout), true))
- throw new InvalidOperationException (
- "Timeout expired. The timeout period elapsed before a " +
- "connection could be obtained. A possible explanation " +
- "is that all the connections in the pool are in use, " +
- "and the maximum pool size is reached.");
- goto retry;
+ if (create_new) {
+ try {
+ result = manager.CreateConnection (info);
+ lock (conns)
+ conns.Add (result);
+ return result;
+ } finally {
+ lock (available)
+ in_progress--;
+ }
}
+ }
- } while (connection == null);
-
- return connection;
+ bool remove_cnc = true;
+ Exception exc = null;
+ try {
+ remove_cnc = (!result.IsConnected || !result.Reset ());
+ } catch (Exception e) {
+ remove_cnc = true;
+ exc = e;
+ }
+ if (remove_cnc) {
+ lock (conns)
+ conns.Remove (result);
+ result.Disconnect ();
+ retries--;
+ if (retries == 0)
+ throw exc;
+ result = null;
+ goto retry;
+ }
+ return result;
}
public void ReleaseConnection (Tds connection)
{
- connection.poolStatus = 0;
- connAvailable.Set ();
- }
-
-#if NET_2_0
- public void ReleaseConnection (ref Tds connection)
- {
- if (pooling == false) {
- int index = Array.IndexOf (list, connection);
- ThreadPool.QueueUserWorkItem (new WaitCallback (DestroyConnection), connection);
- list [index] = connection = null;
- } else {
- connection.poolStatus = 0;
+ if (connection == null)
+ return;
+ if (no_pooling) {
+ connection.Disconnect ();
+ return;
}
- connAvailable.Set ();
- }
-
- public void ResetConnectionPool ()
- {
- Tds connection = null;
- int index = list.Length - 1;
- while (index >= 0)
- {
- connection = list [index];
-
- // skip free slots
- if (connection == null) {
- index--;
- continue;
- }
-
- if (Interlocked.CompareExchange (ref connection.poolStatus, 1, 0) == 0)
- ThreadPool.QueueUserWorkItem (new WaitCallback (DestroyConnection), connection);
- connection.Pooling = false;
-
- list [index] = connection = null;
- connAvailable.Set ();
-
- index--;
+ if (connection.poolStatus == 2) {
+ lock (conns)
+ conns.Remove (connection);
+ connection.Disconnect ();
+ connection = null;
}
- }
-
- public void ResetConnectionPool (Tds connection)
- {
- int index = Array.IndexOf (list, connection);
-
- if (index != -1) {
- if (connection != null && !connection.Reset ()) {
- ThreadPool.QueueUserWorkItem (new WaitCallback (DestroyConnection), connection);
- list [index] = connection = null;
- connAvailable.Set ();
- }
+ lock (available) {
+ if (connection != null) // connection is still open
+ available.Enqueue (connection);
+ // We pulse even if we don't queue, because null means that there's a slot
+ // available in 'conns'
+ Monitor.Pulse (available);
}
}
-#endif
- Tds CreateConnection ()
- {
- return manager.CreateConnection (info);
- }
-
- void DestroyConnection (object state)
+#if NET_2_0
+ public void ResetConnectionPool ()
{
- Tds connection = state as Tds;
- if (connection != null) {
- try {
- connection.Disconnect ();
- } finally {
- connection = null;
+ lock (available) {
+ lock (conns) {
+ Tds tds;
+ int i;
+ for (i = conns.Count - 1; i >= 0; i--) {
+ tds = (Tds) conns [i];
+ tds.poolStatus = 2; // 2 -> disconnect me upon release
+ }
+ for (i = available.Count - 1; i >= 0; i--) {
+ tds = (Tds) available.Dequeue ();
+ tds.Disconnect ();
+ conns.Remove (tds);
+ }
+ available.Clear ();
+ InitializePool ();
}
+ Monitor.PulseAll (available);
}
}
-
+#endif
#endregion // Methods
}
}
+