This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / Npgsql / Npgsql / NpgsqlConnectorPool.cs
1 //      Copyright (C) 2002 The Npgsql Development Team
2 //      npgsql-general@gborg.postgresql.org
3 //      http://gborg.postgresql.org/project/npgsql/projdisplay.php
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 //
19 //      ConnectorPool.cs
20 // ------------------------------------------------------------------
21 //      Status
22 //              0.00.0000 - 06/17/2002 - ulrich sprick - creation
23 //                        - 05/??/2004 - Glen Parker<glenebob@nwlink.com> rewritten using
24 //                               System.Queue.
25
26 using System;
27 using System.Collections;
28 using Npgsql;
29 using System.Threading;
30
31 namespace Npgsql
32 {
33     internal class ConnectorPool
34     {
35         /// <summary>
36         /// A queue with an extra Int32 for keeping track of busy connections.
37         /// </summary>
38         private class ConnectorQueue : System.Collections.Queue
39         {
40             /// <summary>
41             /// The number of pooled Connectors that belong to this queue but
42             /// are currently in use.
43             /// </summary>
44             public Int32            UseCount = 0;
45         }
46
47         /// <value>Unique static instance of the connector pool
48         /// mamager.</value>
49         internal static ConnectorPool ConnectorPoolMgr = new Npgsql.ConnectorPool();
50
51         public ConnectorPool()
52         {
53             PooledConnectors = new Hashtable();
54         }
55
56
57         /// <value>Map of index to unused pooled connectors, avaliable to the
58         /// next RequestConnector() call.</value>
59         /// <remarks>This hashmap will be indexed by connection string.
60         /// This key will hold a list of queues of pooled connectors available to be used.</remarks>
61         private Hashtable PooledConnectors;
62
63         /// <value>Map of shared connectors, avaliable to the
64         /// next RequestConnector() call.</value>
65         /// <remarks>This hashmap will be indexed by connection string.
66         /// This key will hold a list of shared connectors available to be used.</remarks>
67         // To be implemented
68         //private Hashtable SharedConnectors;
69
70         /// <summary>
71         /// Searches the shared and pooled connector lists for a
72         /// matching connector object or creates a new one.
73         /// </summary>
74         /// <param name="Connection">The NpgsqlConnection that is requesting
75         /// the connector. Its ConnectionString will be used to search the
76         /// pool for available connectors.</param>
77         /// <returns>A connector object.</returns>
78         internal Npgsql.Connector RequestConnector (NpgsqlConnection Connection)
79         {
80             if (Connection.Pooling) {
81                 return RequestPooledConnector(Connection);
82             } else {
83                 return GetNonPooledConnector(Connection);
84             }
85         }
86
87         /// <summary>
88         /// Find a pooled connector.  Handle locking and timeout here.
89         /// </summary>
90         private Npgsql.Connector RequestPooledConnector (NpgsqlConnection Connection)
91         {
92             Connector       Connector;
93             Int32           timeoutMilliseconds = Connection.Timeout * 1000;
94
95             lock(this)
96             {
97                 Connector = RequestPooledConnectorInternal(Connection);
98             }
99
100             while (Connector == null && timeoutMilliseconds > 0)
101             {
102                 Int32 ST = timeoutMilliseconds > 1000 ? 1000 : timeoutMilliseconds;
103
104                 Thread.Sleep(ST);
105                 timeoutMilliseconds -= ST;
106
107                 lock(this)
108                 {
109                     Connector = RequestPooledConnectorInternal(Connection);
110                 }
111             }
112
113             if (Connector == null) {
114                 throw new Exception("Timeout while getting a connection from pool.");
115             }
116
117             return Connector;
118         }
119
120         /// <summary>
121         /// Find a pooled connector.  Handle shared/non-shared here.
122         /// </summary>
123         private Npgsql.Connector RequestPooledConnectorInternal (NpgsqlConnection Connection)
124         {
125             Connector       Connector = null;
126             Boolean         Shared = false;
127
128             // If sharing were implemented, I suppose Shared would be set based
129             // on some property on the Connection.
130
131             if (! Shared) {
132                 Connector = GetPooledConnector(Connection);
133             } else {
134                 // Connection sharing? What's that?
135                 throw new NotImplementedException("Internal: Shared pooling not implemented");
136             }
137
138             return Connector;
139         }
140
141         /// <summary>
142         /// Releases a connector, possibly back to the pool for future use.
143         /// </summary>
144         /// <remarks>
145         /// Pooled connectors will be put back into the pool if there is room.
146         /// Shared connectors should just have their use count decremented
147         /// since they always stay in the shared pool.
148         /// </remarks>
149         /// <param name="Connector">The connector to release.</param>
150         /// <param name="ForceClose">Force the connector to close, even if it is pooled.</param>
151         public void ReleaseConnector (Connector Connector, bool ForceClose)
152         {
153             if (Connector.Connection.Pooling) {
154                 ReleasePooledConnector(Connector, ForceClose);
155             } else {
156                 UngetNonPooledConnector(Connector);
157             }
158         }
159
160         /// <summary>
161         /// Release a pooled connector.  Handle locking here.
162         /// </summary>
163         private void ReleasePooledConnector (Connector Connector, bool ForceClose)
164         {
165             lock(this)
166             {
167                 ReleasePooledConnectorInternal(Connector, ForceClose);
168             }
169         }
170
171         /// <summary>
172         /// Release a pooled connector.  Handle shared/non-shared here.
173         /// </summary>
174         private void ReleasePooledConnectorInternal (Connector Connector, bool ForceClose)
175         {
176             if (! Connector.Shared) {
177                 UngetPooledConnector(Connector, ForceClose);
178             } else {
179                 // Connection sharing? What's that?
180                 throw new NotImplementedException("Internal: Shared pooling not implemented");
181             }
182         }
183
184         /// <summary>
185         /// Create a connector without any pooling functionality.
186         /// </summary>
187         private Npgsql.Connector GetNonPooledConnector(NpgsqlConnection Connection)
188         {
189             Connector       Connector;
190
191             Connector = new Connector(false);
192             Connector.Connection = Connection;
193
194             return Connector;
195         }
196
197         /// <summary>
198         /// Find an available pooled connector in the non-shared pool, or create
199         /// a new one if none found.
200         /// </summary>
201         private Npgsql.Connector GetPooledConnector(NpgsqlConnection Connection)
202         {
203             ConnectorQueue  Queue;
204             Connector       Connector = null;
205
206             // Try to find a queue.
207             Queue = (ConnectorQueue)PooledConnectors[Connection.ConnectionString];
208
209             if (Queue == null) {
210                 Queue = new ConnectorQueue();
211                 PooledConnectors[Connection.ConnectionString] = Queue;
212             }
213
214                         if (Queue.Count > 0) {
215                 // Found a queue with connectors.  Grab the top one.
216                 Connector = (Connector)Queue.Dequeue();
217                 Queue.UseCount++;
218                 Connector.Connection = Connection;
219             } else if (Queue.Count + Queue.UseCount < Connection.MaxPoolSize) {
220                 Connector = new Connector(false);
221                 Queue.UseCount++;
222                 Connector.Connection = Connection;
223             }
224             
225             
226             return Connector;
227         }
228
229         /// <summary>
230         /// Find an available shared connector in the shared pool, or create
231         /// a new one if none found.
232         /// </summary>
233         private Npgsql.Connector GetSharedConnector(NpgsqlConnection Connection)
234         {
235             // To be implemented
236
237             return null;
238         }
239
240         /// <summary>
241         /// Close the connector.
242         /// </summary>
243         /// <param name="Connector">Connector to release</param>
244         private void UngetNonPooledConnector(Connector Connector)
245         {
246             Connector.Close();
247         }
248
249         /// <summary>
250         /// Put a pooled connector into the pool queue.  Create the queue if needed.
251         /// </summary>
252         /// <param name="Connector">Connector to pool</param>
253         private void UngetPooledConnector(Connector Connector, bool ForceClose)
254         {
255             ConnectorQueue           Queue;
256
257             // Find the queue.
258             Queue = (ConnectorQueue)PooledConnectors[Connector.Connection.ConnectionString];
259
260             if (Queue == null) {
261                 throw new InvalidOperationException("Internal: No connector queue found for existing connector.");
262             }
263
264             if (ForceClose) {
265                 Connector.Close();
266             } else {
267                 Connector.Connection = null;
268                 Queue.Enqueue(Connector);
269             }
270
271             Queue.UseCount--;
272         }
273
274         /// <summary>
275         /// Stop sharing a shared connector.
276         /// </summary>
277         /// <param name="Connector">Connector to unshare</param>
278         private void UngetSharedConnector(Connector Connector)
279         {
280             // To be implemented
281         }
282     }
283 }