Merge pull request #1163 from AerisG222/more_mvc_fixes
[mono.git] / mcs / class / Mono.Data.Tds / Mono.Data.Tds.Protocol / TdsConnectionPool.cs
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.
11
12 //
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.
31 //
32
33 using System;
34 using System.Collections;
35 using System.Text;
36 using System.Threading;
37
38 namespace Mono.Data.Tds.Protocol 
39 {
40         public class TdsConnectionPoolManager
41         {
42                 Hashtable pools = Hashtable.Synchronized (new Hashtable ());
43                 TdsVersion version;
44                 
45                 public TdsConnectionPoolManager (TdsVersion version)
46                 {
47                         this.version = version;
48                 }
49                 
50                 public TdsConnectionPool GetConnectionPool (string connectionString, TdsConnectionInfo info)
51                 {
52                         TdsConnectionPool pool = (TdsConnectionPool) pools [connectionString];
53                         if (pool == null) {
54                                 pools [connectionString] = new TdsConnectionPool (this, info);
55                                 pool = (TdsConnectionPool) pools [connectionString];
56                         }
57                         return pool;
58                 }
59
60                 public TdsConnectionPool GetConnectionPool (string connectionString)
61                 {
62                         return (TdsConnectionPool) pools [connectionString];
63                 }
64
65                 public virtual Tds CreateConnection (TdsConnectionInfo info)
66                 {
67                         //Console.WriteLine ("CreateConnection: TdsVersion:{0}", version);
68                         switch (version)
69                         {
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, info.LifeTime);
76                                 case TdsVersion.tds80:
77                                         return new Tds80 (info.DataSource, info.Port, info.PacketSize, info.Timeout, info.LifeTime);
78                         }
79                         throw new NotSupportedException ();
80                 }
81
82                 public IDictionary GetConnectionPool ()
83                 {
84                         return pools;
85                 }
86         }
87         
88         public class TdsConnectionInfo
89         {
90                 [Obsolete ("Use the constructor that receives a lifetime parameter")]
91                 public TdsConnectionInfo (string dataSource, int port, int packetSize, int timeout, int minSize, int maxSize)
92                         : this (dataSource, port, packetSize, timeout, minSize, maxSize, 0)
93                 {
94                 }
95
96                 public TdsConnectionInfo (string dataSource, int port, int packetSize, int timeout, int minSize, int maxSize, int lifeTime)
97                 {
98                         DataSource = dataSource;
99                         Port = port;
100                         PacketSize = packetSize;
101                         Timeout = timeout;
102                         PoolMinSize = minSize;
103                         PoolMaxSize = maxSize;
104                         LifeTime = lifeTime;
105                 }
106                 
107                 public string DataSource;
108                 public int Port;
109                 public int PacketSize;
110                 public int Timeout;
111                 public int LifeTime;
112                 public int PoolMinSize;
113                 public int PoolMaxSize;
114
115                 public override string ToString ()
116                 {
117                         StringBuilder sb = new StringBuilder ();
118                         sb.AppendFormat ("DataSouce: {0}\n", DataSource);
119                         sb.AppendFormat ("Port: {0}\n", Port);
120                         sb.AppendFormat ("PacketSize: {0}\n", PacketSize);
121                         sb.AppendFormat ("Timeout: {0}\n", Timeout);
122                         sb.AppendFormat ("PoolMinSize: {0}\n", PoolMinSize);
123                         sb.AppendFormat ("PoolMaxSize: {0}", PoolMaxSize);
124                         return sb.ToString ();
125                 }
126         }
127
128         public class TdsConnectionPool
129         {
130                 TdsConnectionInfo info;
131                 bool no_pooling;
132                 TdsConnectionPoolManager manager;
133                 Queue available;
134                 ArrayList conns;
135                 
136                 public TdsConnectionPool (TdsConnectionPoolManager manager, TdsConnectionInfo info)
137                 {
138                         this.info = info;
139                         this.manager = manager;
140                         conns = new ArrayList (info.PoolMaxSize);
141                         available = new Queue (info.PoolMaxSize);
142                         InitializePool ();
143                 }
144
145                 void InitializePool ()
146                 {
147                         /* conns.Count might not be 0 when we are resetting the connection pool */
148                         for (int i = conns.Count; i < info.PoolMinSize; i++) {
149                                 try {
150                                         Tds t = manager.CreateConnection (info);
151                                         conns.Add (t);
152                                         available.Enqueue (t);
153                                 } catch {
154                                         // Ignore. GetConnection will throw again.
155                                 }
156                         }
157                 }
158
159                 public bool Pooling {
160                         get { return !no_pooling; }
161                         set { no_pooling = !value; }
162                 }
163
164                 #region Methods
165
166                 int in_progress;
167                 public Tds GetConnection ()
168                 {
169                         if (no_pooling)
170                                 return manager.CreateConnection (info);
171
172                         Tds result = null;
173                         bool create_new;
174                         int retries = info.PoolMaxSize * 2;
175 retry:
176                         while (result == null) {
177                                 create_new = false;
178                                 lock (available) {
179                                         if (available.Count > 0) {
180                                                 result = (Tds) available.Dequeue ();
181                                                 break; // .. and do the reset out of the loop
182                                         }
183                                         Monitor.Enter (conns);
184                                         try {
185                                                 if (conns.Count >= info.PoolMaxSize - in_progress) {
186                                                         Monitor.Exit (conns);
187                                                         bool got_lock = Monitor.Wait (available, info.Timeout * 1000);
188                                                         if (!got_lock) {
189                                                                 throw new InvalidOperationException (
190                                                                         "Timeout expired. The timeout period elapsed before a " +
191                                                                         "connection could be obtained. A possible explanation " +
192                                                                         "is that all the connections in the pool are in use, " +
193                                                                         "and the maximum pool size is reached.");
194                                                         } else if (available.Count > 0) {
195                                                                 result = (Tds) available.Dequeue ();
196                                                                 break; // .. and do the reset out of the loop
197                                                         }
198                                                         continue;
199                                                 } else {
200                                                         create_new = true;
201                                                         in_progress++;
202                                                 }
203                                         } finally {
204                                                 Monitor.Exit (conns); // Exiting if not owned is ok < 2.x
205                                         }
206                                 }
207                                 if (create_new) {
208                                         try {
209                                                 result = manager.CreateConnection (info);
210                                                 lock (conns)
211                                                         conns.Add (result);
212                                                 return result;
213                                         } finally {
214                                                 lock (available)
215                                                         in_progress--;
216                                         }
217                                 }
218                         }
219
220                         bool remove_cnc = true;
221                         Exception exc = null;
222                         try {
223                                 remove_cnc = (!result.IsConnected || !result.Reset ());
224                         } catch (Exception e) {
225                                 remove_cnc = true;
226                                 exc = e;
227                         }
228                         if (remove_cnc) {
229                                 lock (conns)
230                                         conns.Remove (result);
231                                 result.Disconnect ();
232                                 retries--;
233                                 if (retries == 0)
234                                         throw exc;
235                                 result = null;
236                                 goto retry;
237                         }
238                         return result;
239                 }
240
241                 public void ReleaseConnection (Tds connection)
242                 {
243                         if (connection == null)
244                                 return;
245                         if (no_pooling) {
246                                 connection.Disconnect ();
247                                 return;
248                         }
249
250                         if (connection.poolStatus == 2 || connection.Expired) {
251                                 lock (conns)
252                                         conns.Remove (connection);
253                                 connection.Disconnect ();
254                                 connection = null;
255                         }
256                         lock (available) {
257                                 if (connection != null) // connection is still open
258                                         available.Enqueue (connection);
259                                 // We pulse even if we don't queue, because null means that there's a slot
260                                 // available in 'conns'
261                                 Monitor.Pulse (available);
262                         }
263                 }
264
265 #if NET_2_0
266                 public void ResetConnectionPool ()
267                 {
268                         lock (available) {
269                                 lock (conns) {
270                                         Tds tds;
271                                         int i;
272                                         for (i = conns.Count - 1; i >= 0; i--) {
273                                                 tds = (Tds) conns [i];
274                                                 tds.poolStatus = 2; // 2 -> disconnect me upon release
275                                         }
276                                         for (i = available.Count - 1; i >= 0; i--) {
277                                                 tds = (Tds) available.Dequeue ();
278                                                 tds.Disconnect ();
279                                                 conns.Remove (tds);
280                                         }
281                                         available.Clear ();
282                                         InitializePool ();
283                                 }
284                                 Monitor.PulseAll (available);
285                         }
286                 }
287 #endif
288                 #endregion // Methods
289         }
290 }
291