2 * Firebird ADO.NET Data provider for .NET and Mono
4 * The contents of this file are subject to the Initial
5 * Developer's Public License Version 1.0 (the "License");
6 * you may not use this file except in compliance with the
7 * License. You may obtain a copy of the License at
8 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
10 * Software distributed under the License is distributed on
11 * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
12 * express or implied. See the License for the specific
13 * language governing rights and limitations under the License.
15 * Copyright (c) 2002, 2005 Carlos Guzman Alvarez
16 * All Rights Reserved.
21 using System.Collections;
22 using System.Threading;
24 using FirebirdSql.Data.Common;
26 namespace FirebirdSql.Data.Firebird
28 internal sealed class FbPoolManager
32 public static readonly FbPoolManager Instance = new FbPoolManager();
38 private Hashtable pools;
39 private Hashtable handlers;
40 private object syncObject;
50 if (this.pools != null)
52 return this.pools.Count;
62 private FbPoolManager()
64 this.pools = Hashtable.Synchronized(new Hashtable());
65 this.handlers = Hashtable.Synchronized(new Hashtable());
66 this.syncObject = new object();
73 public FbConnectionPool FindPool(string connectionString)
75 FbConnectionPool pool = null;
77 lock (this.syncObject)
79 if (this.pools.ContainsKey(connectionString.GetHashCode()))
81 pool = (FbConnectionPool)pools[connectionString.GetHashCode()];
88 public FbConnectionPool CreatePool(string connectionString)
90 FbConnectionPool pool = null;
92 lock (this.syncObject)
94 pool = this.FindPool(connectionString);
98 lock (this.pools.SyncRoot)
100 int hashcode = connectionString.GetHashCode();
102 // Create an empty pool handler
103 EmptyPoolEventHandler handler = new EmptyPoolEventHandler(this.OnEmptyPool);
105 this.handlers.Add(hashcode, handler);
107 // Create the new connection pool
108 pool = new FbConnectionPool(connectionString);
110 this.pools.Add(hashcode, pool);
112 pool.EmptyPool += handler;
120 public void ClearAllPools()
122 lock (this.syncObject)
124 lock (this.pools.SyncRoot)
126 FbConnectionPool[] tempPools = new FbConnectionPool[this.pools.Count];
128 this.pools.Values.CopyTo(tempPools, 0);
130 foreach (FbConnectionPool pool in tempPools)
138 this.handlers.Clear();
143 public void ClearPool(string connectionString)
145 lock (this.syncObject)
147 lock (this.pools.SyncRoot)
149 int hashCode = connectionString.GetHashCode();
151 if (this.pools.ContainsKey(hashCode))
153 FbConnectionPool pool = (FbConnectionPool)this.pools[hashCode];
164 #region Private Methods
166 private void OnEmptyPool(object sender, EventArgs e)
168 lock (this.pools.SyncRoot)
170 int hashCode = (int)sender;
172 if (this.pools.ContainsKey(hashCode))
174 FbConnectionPool pool = (FbConnectionPool)this.pools[hashCode];
175 EmptyPoolEventHandler handler = (EmptyPoolEventHandler)this.handlers[hashCode];
177 pool.EmptyPool -= handler;
179 this.pools.Remove(hashCode);
180 this.handlers.Remove(hashCode);
191 internal delegate void EmptyPoolEventHandler(object sender, EventArgs e);
193 internal class FbConnectionPool : MarshalByRefObject
197 private FbConnectionString options;
198 private ArrayList locked;
199 private ArrayList unlocked;
200 private Thread cleanUpThread;
201 private string connectionString;
202 private bool isRunning;
203 private long lifeTime;
204 private object syncObject;
210 public event EmptyPoolEventHandler EmptyPool;
218 get { return this.unlocked.Count + this.locked.Count; }
225 public FbConnectionPool(string connectionString)
227 this.syncObject = new object();
228 this.connectionString = connectionString;
229 this.options = new FbConnectionString(connectionString);
230 this.lifeTime = this.options.ConnectionLifeTime * TimeSpan.TicksPerSecond;
232 if (this.options.MaxPoolSize == 0)
234 this.locked = ArrayList.Synchronized(new ArrayList());
235 this.unlocked = ArrayList.Synchronized(new ArrayList());
239 this.locked = ArrayList.Synchronized(new ArrayList(this.options.MaxPoolSize));
240 this.unlocked = ArrayList.Synchronized(new ArrayList(this.options.MaxPoolSize));
243 // If a minimun number of connections is requested
244 // initialize the pool
247 // Start the cleanup thread only if needed
248 if (this.lifeTime != 0)
250 this.isRunning = true;
252 this.cleanUpThread = new Thread(new ThreadStart(this.RunCleanup));
253 this.cleanUpThread.Name = "Cleanup Thread";
254 this.cleanUpThread.Start();
255 this.cleanUpThread.IsBackground = true;
263 public void CheckIn(FbConnectionInternal connection)
265 connection.OwningConnection = null;
266 connection.Created = System.DateTime.Now.Ticks;
268 this.locked.Remove(connection);
269 this.unlocked.Add(connection);
272 public FbConnectionInternal CheckOut()
274 FbConnectionInternal newConnection = null;
276 lock (this.syncObject)
278 this.CheckMaxPoolSize();
280 lock (this.unlocked.SyncRoot)
282 newConnection = this.GetConnection();
283 if (newConnection != null)
285 return newConnection;
289 newConnection = this.Create();
291 // Set connection pooling settings to the new connection
292 newConnection.Lifetime = this.options.ConnectionLifeTime;
293 newConnection.Pooled = true;
295 // Added to the locked connections list.
296 this.locked.Add(newConnection);
299 return newConnection;
304 lock (this.syncObject)
306 // Stop cleanup thread
307 if (this.cleanUpThread != null)
309 this.cleanUpThread.Abort();
310 this.cleanUpThread.Join();
313 // Close all unlocked connections
314 FbConnectionInternal[] list = (FbConnectionInternal[])this.unlocked.ToArray(typeof(FbConnectionInternal));
316 foreach (FbConnectionInternal connection in list)
318 connection.Disconnect();
321 // Close all locked connections
322 list = (FbConnectionInternal[])this.locked.ToArray(typeof(FbConnectionInternal));
324 foreach (FbConnectionInternal connection in list)
326 connection.Disconnect();
330 this.unlocked.Clear();
333 // Raise EmptyPool event
334 if (this.EmptyPool != null)
336 this.EmptyPool(this.connectionString.GetHashCode(), null);
340 this.unlocked = null;
342 this.connectionString = null;
343 this.cleanUpThread = null;
344 this.EmptyPool = null;
350 #region Private Methods
352 private bool CheckMinPoolSize()
354 if (this.options.MinPoolSize > 0 && this.Count == this.options.MinPoolSize)
364 private void CheckMaxPoolSize()
366 lock (this.syncObject)
368 if (this.options.MaxPoolSize > 0 &&
369 (this.Count + 1) >= this.options.MaxPoolSize)
371 long timeout = this.options.ConnectionTimeout * TimeSpan.TicksPerSecond;
372 long start = DateTime.Now.Ticks;
376 if ((this.Count + 1) >= this.options.MaxPoolSize)
378 if ((DateTime.Now.Ticks - start) > timeout)
380 throw new SystemException("Timeout exceeded.");
394 private void Initialize()
396 lock (this.syncObject)
398 for (int i = 0; i < this.options.MinPoolSize; i++)
400 this.unlocked.Add(this.Create());
405 private FbConnectionInternal Create()
407 FbConnectionInternal connection = new FbConnectionInternal(this.options);
408 connection.Connect();
410 connection.Pooled = true;
411 connection.Created = DateTime.Now.Ticks;
416 private FbConnectionInternal GetConnection()
418 FbConnectionInternal[] list = (FbConnectionInternal[])this.unlocked.ToArray(typeof(FbConnectionInternal));
419 FbConnectionInternal result = null;
424 foreach (FbConnectionInternal connection in list)
426 if (connection.Verify())
428 if (this.lifeTime != 0)
430 long now = DateTime.Now.Ticks;
431 long expire = connection.Created + this.lifeTime;
435 if (this.CheckMinPoolSize())
437 this.unlocked.Remove(connection);
438 this.Expire(connection);
458 this.unlocked.Remove(connection);
459 this.Expire(connection);
465 this.unlocked.Remove(result);
466 this.locked.Add(result);
472 private void RunCleanup()
474 int interval = Convert.ToInt32(TimeSpan.FromTicks(this.lifeTime).TotalMilliseconds);
476 if (interval > 60000)
483 while (this.isRunning)
485 Thread.Sleep(interval);
491 lock (this.syncObject)
494 if (this.EmptyPool != null)
496 this.EmptyPool(this.connectionString.GetHashCode(), null);
500 this.isRunning = false;
505 catch (ThreadAbortException)
507 this.isRunning = false;
511 private void Expire(FbConnectionInternal connection)
515 if (connection.Verify())
517 connection.Disconnect();
522 throw new FbException("Error closing database connection.");
526 private void Cleanup()
528 lock (this.unlocked.SyncRoot)
530 if (this.unlocked.Count > 0 && this.lifeTime != 0)
532 FbConnectionInternal[] list = (FbConnectionInternal[])this.unlocked.ToArray(typeof(FbConnectionInternal));
534 foreach (FbConnectionInternal connection in list)
536 long now = DateTime.Now.Ticks;
537 long expire = connection.Created + this.lifeTime;
541 if (this.CheckMinPoolSize())
543 this.unlocked.Remove(connection);
544 this.Expire(connection);