flush
[mcs.git] / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / TdsConnectionPool.cs
blob30636d4d2ba3f59ea00547158a9909ffe68a107c
1 //
2 // Mono.Data.TdsClient.TdsConnectionPool.cs
3 //
4 // Author:
5 // Lluis Sanchez Gual (lluis@ximian.com)
6 // Christian Hergert (christian.hergert@gmail.com)
7 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 //
9 // Copyright (C) 2004 Novell, Inc.
10 // Copyright (C) 2009 Novell, Inc.
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System;
34 using System.Collections;
35 using System.Text;
36 using System.Threading;
38 namespace Mono.Data.Tds.Protocol
40 public class TdsConnectionPoolManager
42 Hashtable pools = Hashtable.Synchronized (new Hashtable ());
43 TdsVersion version;
45 public TdsConnectionPoolManager (TdsVersion version)
47 this.version = version;
50 public TdsConnectionPool GetConnectionPool (string connectionString, TdsConnectionInfo info)
52 TdsConnectionPool pool = (TdsConnectionPool) pools [connectionString];
53 if (pool == null) {
54 pools [connectionString] = new TdsConnectionPool (this, info);
55 pool = (TdsConnectionPool) pools [connectionString];
57 return pool;
60 public TdsConnectionPool GetConnectionPool (string connectionString)
62 return (TdsConnectionPool) pools [connectionString];
65 public virtual Tds CreateConnection (TdsConnectionInfo info)
67 //Console.WriteLine ("CreateConnection: TdsVersion:{0}", version);
68 switch (version)
70 case TdsVersion.tds42:
71 return new Tds42 (info.DataSource, info.Port, info.PacketSize, info.Timeout);
72 case TdsVersion.tds50:
73 return new Tds50 (info.DataSource, info.Port, info.PacketSize, info.Timeout);
74 case TdsVersion.tds70:
75 return new Tds70 (info.DataSource, info.Port, info.PacketSize, info.Timeout);
76 case TdsVersion.tds80:
77 return new Tds80 (info.DataSource, info.Port, info.PacketSize, info.Timeout);
79 throw new NotSupportedException ();
82 public IDictionary GetConnectionPool ()
84 return pools;
88 public class TdsConnectionInfo
90 public TdsConnectionInfo (string dataSource, int port, int packetSize, int timeout, int minSize, int maxSize)
92 DataSource = dataSource;
93 Port = port;
94 PacketSize = packetSize;
95 Timeout = timeout;
96 PoolMinSize = minSize;
97 PoolMaxSize = maxSize;
100 public string DataSource;
101 public int Port;
102 public int PacketSize;
103 public int Timeout;
104 public int PoolMinSize;
105 public int PoolMaxSize;
107 public override string ToString ()
109 StringBuilder sb = new StringBuilder ();
110 sb.AppendFormat ("DataSouce: {0}\n", DataSource);
111 sb.AppendFormat ("Port: {0}\n", Port);
112 sb.AppendFormat ("PacketSize: {0}\n", PacketSize);
113 sb.AppendFormat ("Timeout: {0}\n", Timeout);
114 sb.AppendFormat ("PoolMinSize: {0}\n", PoolMinSize);
115 sb.AppendFormat ("PoolMaxSize: {0}", PoolMaxSize);
116 return sb.ToString ();
120 public class TdsConnectionPool
122 TdsConnectionInfo info;
123 bool no_pooling;
124 TdsConnectionPoolManager manager;
125 Queue available;
126 ArrayList conns;
128 public TdsConnectionPool (TdsConnectionPoolManager manager, TdsConnectionInfo info)
130 this.info = info;
131 this.manager = manager;
132 conns = new ArrayList (info.PoolMaxSize);
133 available = new Queue (info.PoolMaxSize);
134 InitializePool ();
137 void InitializePool ()
139 /* conns.Count might not be 0 when we are resetting the connection pool */
140 for (int i = conns.Count; i < info.PoolMinSize; i++) {
141 try {
142 Tds t = manager.CreateConnection (info);
143 conns.Add (t);
144 available.Enqueue (t);
145 } catch {
146 // Ignore. GetConnection will throw again.
151 public bool Pooling {
152 get { return !no_pooling; }
153 set { no_pooling = !value; }
156 #region Methods
158 int in_progress;
159 public Tds GetConnection ()
161 if (no_pooling)
162 return manager.CreateConnection (info);
164 Tds result = null;
165 bool create_new;
166 int retries = info.PoolMaxSize * 2;
167 retry:
168 while (result == null) {
169 create_new = false;
170 lock (available) {
171 if (available.Count > 0) {
172 result = (Tds) available.Dequeue ();
173 break; // .. and do the reset out of the loop
175 Monitor.Enter (conns);
176 try {
177 if (conns.Count >= info.PoolMaxSize - in_progress) {
178 Monitor.Exit (conns);
179 bool got_lock = Monitor.Wait (available, info.Timeout * 1000);
180 if (!got_lock) {
181 throw new InvalidOperationException (
182 "Timeout expired. The timeout period elapsed before a " +
183 "connection could be obtained. A possible explanation " +
184 "is that all the connections in the pool are in use, " +
185 "and the maximum pool size is reached.");
186 } else if (available.Count > 0) {
187 result = (Tds) available.Dequeue ();
188 break; // .. and do the reset out of the loop
190 continue;
191 } else {
192 create_new = true;
193 in_progress++;
195 } finally {
196 Monitor.Exit (conns); // Exiting if not owned is ok < 2.x
199 if (create_new) {
200 try {
201 result = manager.CreateConnection (info);
202 lock (conns)
203 conns.Add (result);
204 return result;
205 } finally {
206 lock (available)
207 in_progress--;
212 bool remove_cnc = true;
213 Exception exc = null;
214 try {
215 remove_cnc = (!result.IsConnected || !result.Reset ());
216 } catch (Exception e) {
217 remove_cnc = true;
218 exc = e;
220 if (remove_cnc) {
221 lock (conns)
222 conns.Remove (result);
223 result.Disconnect ();
224 retries--;
225 if (retries == 0)
226 throw exc;
227 goto retry;
229 return result;
232 public void ReleaseConnection (Tds connection)
234 if (connection == null)
235 return;
236 if (no_pooling) {
237 connection.Disconnect ();
238 return;
241 if (connection.poolStatus == 2) {
242 lock (conns)
243 conns.Remove (connection);
244 connection.Disconnect ();
245 connection = null;
247 lock (available) {
248 if (connection != null) // connection is still open
249 available.Enqueue (connection);
250 // We pulse even if we don't queue, because null means that there's a slot
251 // available in 'conns'
252 Monitor.Pulse (available);
256 #if NET_2_0
257 public void ResetConnectionPool ()
259 lock (available) {
260 lock (conns) {
261 Tds tds;
262 int i;
263 for (i = conns.Count - 1; i >= 0; i--) {
264 tds = (Tds) conns [i];
265 tds.poolStatus = 2; // 2 -> disconnect me upon release
267 for (i = available.Count - 1; i >= 0; i--) {
268 tds = (Tds) available.Dequeue ();
269 tds.Disconnect ();
270 conns.Remove (tds);
272 available.Clear ();
273 InitializePool ();
275 Monitor.PulseAll (available);
278 #endif
279 #endregion // Methods