2 // Mono.Remoting.Channels.Unix.UnixConnectionPool.cs
\r
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
\r
6 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
\r
10 // Permission is hereby granted, free of charge, to any person obtaining
\r
11 // a copy of this software and associated documentation files (the
\r
12 // "Software"), to deal in the Software without restriction, including
\r
13 // without limitation the rights to use, copy, modify, merge, publish,
\r
14 // distribute, sublicense, and/or sell copies of the Software, and to
\r
15 // permit persons to whom the Software is furnished to do so, subject to
\r
16 // the following conditions:
\r
18 // The above copyright notice and this permission notice shall be
\r
19 // included in all copies or substantial portions of the Software.
\r
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
31 using System.Collections;
\r
32 using System.Threading;
\r
34 using System.Net.Sockets;
\r
35 using System.Runtime.Remoting;
\r
38 namespace Mono.Remoting.Channels.Unix
\r
40 // This is a pool of Unix connections. Connections requested
\r
41 // by the TCP channel are pooled after their use, and can
\r
42 // be reused later. Connections are automaticaly closed
\r
43 // if not used after some time, specified in KeepAliveSeconds.
\r
44 // The number of allowed open connections can also be specified
\r
45 // in MaxOpenConnections. The limit is per host.
\r
46 // If a thread requests a connection and the limit has been
\r
47 // reached, the thread is suspended until one is released.
\r
49 internal class UnixConnectionPool
\r
51 // Table of pools. There is a HostConnectionPool
\r
52 // instance for each host
\r
53 static Hashtable _pools = new Hashtable();
\r
55 static int _maxOpenConnections = int.MaxValue;
\r
56 static int _keepAliveSeconds = 15;
\r
58 static Thread _poolThread;
\r
60 static UnixConnectionPool()
\r
62 // This thread will close unused connections
\r
63 _poolThread = new Thread (new ThreadStart (ConnectionCollector));
\r
64 _poolThread.Start();
\r
65 _poolThread.IsBackground = true;
\r
68 public static void Shutdown ()
\r
70 if (_poolThread != null)
\r
71 _poolThread.Abort();
\r
74 public static int MaxOpenConnections
\r
76 get { return _maxOpenConnections; }
\r
79 if (value < 1) throw new RemotingException ("MaxOpenConnections must be greater than zero");
\r
80 _maxOpenConnections = value;
\r
84 public static int KeepAliveSeconds
\r
86 get { return _keepAliveSeconds; }
\r
87 set { _keepAliveSeconds = value; }
\r
90 public static UnixConnection GetConnection (string path)
\r
92 HostConnectionPool hostPool;
\r
96 hostPool = (HostConnectionPool) _pools[path];
\r
97 if (hostPool == null)
\r
99 hostPool = new HostConnectionPool(path);
\r
100 _pools[path] = hostPool;
\r
104 return hostPool.GetConnection();
\r
107 private static void ConnectionCollector ()
\r
111 Thread.Sleep(3000);
\r
114 ICollection values = _pools.Values;
\r
115 foreach (HostConnectionPool pool in values)
\r
116 pool.PurgeConnections();
\r
122 internal class ReusableUnixClient : UnixClient
\r
124 public ReusableUnixClient (string path): base (path)
\r
128 public bool IsAlive
\r
132 // This Poll will return true if there is data pending to
\r
133 // be read. It prob. means that a client object using this
\r
134 // connection got an exception and did not finish to read
\r
135 // the data. It can also mean that the connection has been
\r
136 // closed in the server. In both cases, the connection cannot
\r
138 return !Client.Poll (0, SelectMode.SelectRead);
\r
143 internal class UnixConnection
\r
145 DateTime _controlTime;
\r
147 ReusableUnixClient _client;
\r
148 HostConnectionPool _pool;
\r
151 public UnixConnection (HostConnectionPool pool, ReusableUnixClient client)
\r
155 _stream = new BufferedStream (client.GetStream());
\r
156 _controlTime = DateTime.Now;
\r
157 _buffer = new byte[UnixMessageIO.DefaultStreamBufferSize];
\r
160 public Stream Stream
\r
162 get { return _stream; }
\r
165 public DateTime ControlTime
\r
167 get { return _controlTime; }
\r
168 set { _controlTime = value; }
\r
171 public bool IsAlive
\r
173 get { return _client.IsAlive; }
\r
176 // This is a "thread safe" buffer that can be used by
\r
177 // UnixClientTransportSink to read or send data to the stream.
\r
178 // The buffer is "thread safe" since only one thread can
\r
179 // use a connection at a given time.
\r
180 public byte[] Buffer
\r
182 get { return _buffer; }
\r
185 // Returns the connection to the pool
\r
186 public void Release()
\r
188 _pool.ReleaseConnection (this);
\r
191 public void Close()
\r
197 internal class HostConnectionPool
\r
199 ArrayList _pool = new ArrayList();
\r
200 int _activeConnections = 0;
\r
204 public HostConnectionPool (string path)
\r
209 public UnixConnection GetConnection ()
\r
211 UnixConnection connection = null;
\r
216 if (_pool.Count > 0)
\r
218 // There are available connections
\r
220 connection = (UnixConnection)_pool[_pool.Count - 1];
\r
221 _pool.RemoveAt(_pool.Count - 1);
\r
222 if (!connection.IsAlive) {
\r
223 CancelConnection (connection);
\r
229 if (connection == null && _activeConnections < UnixConnectionPool.MaxOpenConnections)
\r
231 // No connections available, but the max connections
\r
232 // has not been reached yet, so a new one can be created
\r
233 // Create the connection outside the lock
\r
237 // No available connections in the pool
\r
238 // Wait for somewone to release one.
\r
240 if (connection == null)
\r
242 Monitor.Wait(_pool);
\r
245 while (connection == null);
\r
248 if (connection == null)
\r
249 return CreateConnection ();
\r
254 private UnixConnection CreateConnection()
\r
258 ReusableUnixClient client = new ReusableUnixClient (_path);
\r
259 UnixConnection entry = new UnixConnection(this, client);
\r
260 _activeConnections++;
\r
263 catch (Exception ex)
\r
265 throw new RemotingException (ex.Message);
\r
269 public void ReleaseConnection (UnixConnection entry)
\r
273 entry.ControlTime = DateTime.Now; // Initialize timeout
\r
275 Monitor.Pulse (_pool);
\r
279 private void CancelConnection(UnixConnection entry)
\r
283 entry.Stream.Close();
\r
284 _activeConnections--;
\r
291 public void PurgeConnections()
\r
295 for (int n=0; n < _pool.Count; n++)
\r
297 UnixConnection entry = (UnixConnection)_pool[n];
\r
298 if ( (DateTime.Now - entry.ControlTime).TotalSeconds > UnixConnectionPool.KeepAliveSeconds)
\r
300 CancelConnection (entry);
\r