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, 2004 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 private static FbPoolManager instance;
38 private Hashtable pools;
39 private Hashtable handlers;
43 #region Static Properties
45 public static FbPoolManager Instance
49 if (FbPoolManager.instance == null)
51 FbPoolManager.instance = new FbPoolManager();
54 return FbPoolManager.instance;
66 if (this.pools != null)
68 return this.pools.Count;
78 private FbPoolManager()
80 this.pools = Hashtable.Synchronized(new Hashtable());
81 this.handlers = Hashtable.Synchronized(new Hashtable());
88 public FbConnectionPool FindPool(string connectionString)
90 FbConnectionPool pool = null;
94 if (this.pools.ContainsKey(connectionString.GetHashCode()))
96 pool = (FbConnectionPool)pools[connectionString.GetHashCode()];
103 public FbConnectionPool CreatePool(string connectionString)
105 FbConnectionPool pool = null;
109 pool = this.FindPool(connectionString);
113 lock (this.pools.SyncRoot)
115 int hashcode = connectionString.GetHashCode();
117 // Create an empty pool handler
118 EmptyPoolEventHandler handler = new EmptyPoolEventHandler(this.OnEmptyPool);
120 this.handlers.Add(hashcode, handler);
122 // Create the new connection pool
123 pool = new FbConnectionPool(connectionString);
125 this.pools.Add(hashcode, pool);
127 pool.EmptyPool += handler;
135 public void ClearAllPools()
139 lock (this.pools.SyncRoot)
141 FbConnectionPool[] tempPools = new FbConnectionPool[this.pools.Count];
143 this.pools.Values.CopyTo(tempPools, 0);
145 foreach (FbConnectionPool pool in tempPools)
153 this.handlers.Clear();
158 public void ClearPool(string connectionString)
162 lock (this.pools.SyncRoot)
164 int hashCode = connectionString.GetHashCode();
166 if (this.pools.ContainsKey(hashCode))
168 FbConnectionPool pool = (FbConnectionPool)this.pools[hashCode];
179 #region Private Methods
181 private void OnEmptyPool(object sender, EventArgs e)
183 lock (this.pools.SyncRoot)
185 int hashCode = (int)sender;
187 if (this.pools.ContainsKey(hashCode))
189 FbConnectionPool pool = (FbConnectionPool)this.pools[hashCode];
190 EmptyPoolEventHandler handler = (EmptyPoolEventHandler)this.handlers[hashCode];
192 pool.EmptyPool -= handler;
194 this.pools.Remove(hashCode);
195 this.handlers.Remove(hashCode);
206 internal delegate void EmptyPoolEventHandler(object sender, EventArgs e);
208 internal class FbConnectionPool : MarshalByRefObject
212 private string connectionString;
213 private FbConnectionString options;
214 private ArrayList locked;
215 private ArrayList unlocked;
216 private Thread cleanUpThread;
217 private bool isRunning;
218 private long lifeTime;
224 public event EmptyPoolEventHandler EmptyPool;
232 get { return this.unlocked.Count + this.locked.Count; }
239 public FbConnectionPool(string connectionString)
241 this.connectionString = connectionString;
242 this.options = new FbConnectionString(connectionString);
243 this.lifeTime = this.options.ConnectionLifeTime * TimeSpan.TicksPerSecond;
245 if (this.options.MaxPoolSize == 0)
247 this.locked = ArrayList.Synchronized(new ArrayList());
248 this.unlocked = ArrayList.Synchronized(new ArrayList());
252 this.locked = ArrayList.Synchronized(new ArrayList(this.options.MaxPoolSize));
253 this.unlocked = ArrayList.Synchronized(new ArrayList(this.options.MaxPoolSize));
256 // If a minimun number of connections is requested
257 // initialize the pool
260 // Start the cleanup thread only if needed
261 if (this.lifeTime != 0)
263 this.isRunning = true;
265 this.cleanUpThread = new Thread(new ThreadStart(this.RunCleanup));
266 this.cleanUpThread.Name = "Cleanup Thread";
267 this.cleanUpThread.Start();
268 this.cleanUpThread.IsBackground = true;
276 public void CheckIn(FbConnectionInternal connection)
278 connection.OwningConnection = null;
279 connection.Created = System.DateTime.Now.Ticks;
281 this.locked.Remove(connection);
282 this.unlocked.Add(connection);
285 public FbConnectionInternal CheckOut()
287 FbConnectionInternal newConnection = null;
291 this.CheckMaxPoolSize();
293 lock (this.unlocked.SyncRoot)
295 newConnection = this.GetConnection();
296 if (newConnection != null)
298 return newConnection;
302 newConnection = this.Create();
304 // Set connection pooling settings to the new connection
305 newConnection.Lifetime = this.options.ConnectionLifeTime;
306 newConnection.Pooled = true;
308 // Added to the locked connections list.
309 this.locked.Add(newConnection);
312 return newConnection;
319 // Stop cleanup thread
320 if (this.cleanUpThread != null)
322 this.cleanUpThread.Abort();
323 this.cleanUpThread.Join();
326 // Close all unlocked connections
327 FbConnectionInternal[] list = (FbConnectionInternal[])this.unlocked.ToArray(typeof(FbConnectionInternal));
329 foreach (FbConnectionInternal connection in list)
331 connection.Disconnect();
334 // Close all locked connections
335 list = (FbConnectionInternal[])this.locked.ToArray(typeof(FbConnectionInternal));
337 foreach (FbConnectionInternal connection in list)
339 connection.Disconnect();
343 this.unlocked.Clear();
346 // Raise EmptyPool event
347 if (this.EmptyPool != null)
349 this.EmptyPool(this.connectionString.GetHashCode(), null);
353 this.unlocked = null;
355 this.connectionString = null;
356 this.cleanUpThread = null;
357 this.EmptyPool = null;
363 #region Private Methods
365 private bool CheckMinPoolSize()
367 if (this.options.MinPoolSize > 0 && this.Count == this.options.MinPoolSize)
377 private void CheckMaxPoolSize()
381 if (this.options.MaxPoolSize > 0 &&
382 (this.Count + 1) >= this.options.MaxPoolSize)
384 long timeout = this.options.ConnectionTimeout * TimeSpan.TicksPerSecond;
385 long start = DateTime.Now.Ticks;
389 if ((this.Count + 1) >= this.options.MaxPoolSize)
391 if ((DateTime.Now.Ticks - start) > timeout)
393 throw new SystemException("Timeout exceeded.");
407 private void Initialize()
411 for (int i = 0; i < this.options.MinPoolSize; i++)
413 this.unlocked.Add(this.Create());
418 private FbConnectionInternal Create()
420 FbConnectionInternal connection = new FbConnectionInternal(this.options);
421 connection.Connect();
423 connection.Pooled = true;
424 connection.Created = DateTime.Now.Ticks;
429 private FbConnectionInternal GetConnection()
431 FbConnectionInternal[] list = (FbConnectionInternal[])this.unlocked.ToArray(typeof(FbConnectionInternal));
432 FbConnectionInternal result = null;
437 foreach (FbConnectionInternal connection in list)
439 if (connection.Verify())
441 if (this.lifeTime != 0)
443 long now = DateTime.Now.Ticks;
444 long expire = connection.Created + this.lifeTime;
448 if (this.CheckMinPoolSize())
450 this.unlocked.Remove(connection);
451 this.Expire(connection);
471 this.unlocked.Remove(connection);
472 this.Expire(connection);
478 this.unlocked.Remove(result);
479 this.locked.Add(result);
485 private void RunCleanup()
487 int interval = Convert.ToInt32(TimeSpan.FromTicks(this.lifeTime).TotalMilliseconds);
489 if (interval > 60000)
496 while (this.isRunning)
498 Thread.Sleep(interval);
507 if (this.EmptyPool != null)
509 this.EmptyPool(this.connectionString.GetHashCode(), null);
513 this.isRunning = false;
518 catch (ThreadAbortException)
520 this.isRunning = false;
524 private void Expire(FbConnectionInternal connection)
528 if (connection.Verify())
530 connection.Disconnect();
535 throw new FbException("Error closing database connection.");
539 private void Cleanup()
541 lock (this.unlocked.SyncRoot)
543 if (this.unlocked.Count > 0 && this.lifeTime != 0)
545 FbConnectionInternal[] list = (FbConnectionInternal[])this.unlocked.ToArray(typeof(FbConnectionInternal));
547 foreach (FbConnectionInternal connection in list)
549 long now = DateTime.Now.Ticks;
550 long expire = connection.Created + this.lifeTime;
554 if (this.CheckMinPoolSize())
556 this.unlocked.Remove(connection);
557 this.Expire(connection);