add ISafeSerializationData
[mcs.git] / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Tcp / TcpConnectionPool.cs
blob0e7efffeed4beb9025ae97340b06c7a52f2f314f
1 //
2 // System.Runtime.Remoting.Channels.Tcp.TcpConnectionPool.cs
3 //
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
5 //
6 // 2002 (C) Lluis Sanchez Gual
7 //
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System;
31 using System.Collections;
32 using System.Threading;
33 using System.IO;
34 using System.Net.Sockets;
36 namespace System.Runtime.Remoting.Channels.Tcp
38 // This is a pool of Tcp connections. Connections requested
39 // by the TCP channel are pooled after their use, and can
40 // be reused later. Connections are automaticaly closed
41 // if not used after some time, specified in KeepAliveSeconds.
42 // The number of allowed open connections can also be specified
43 // in MaxOpenConnections. The limit is per host.
44 // If a thread requests a connection and the limit has been
45 // reached, the thread is suspended until one is released.
47 internal class TcpConnectionPool
49 // Table of pools. There is a HostConnectionPool
50 // instance for each host
51 static Hashtable _pools = new Hashtable();
53 static int _maxOpenConnections = int.MaxValue;
54 static int _keepAliveSeconds = 15;
56 static Thread _poolThread;
58 static TcpConnectionPool()
60 // This thread will close unused connections
61 _poolThread = new Thread (new ThreadStart (ConnectionCollector));
62 _poolThread.IsBackground = true;
63 _poolThread.Start();
66 public static void Shutdown ()
68 #if !TARGET_JVM
69 if (_poolThread != null)
70 _poolThread.Abort();
71 #endif
74 public static int MaxOpenConnections
76 get { return _maxOpenConnections; }
77 set
79 if (value < 1) throw new RemotingException ("MaxOpenConnections must be greater than zero");
80 _maxOpenConnections = value;
84 public static int KeepAliveSeconds
86 get { return _keepAliveSeconds; }
87 set { _keepAliveSeconds = value; }
90 public static TcpConnection GetConnection (string host, int port)
92 HostConnectionPool hostPool;
94 lock (_pools)
96 string key = host + ":" + port;
97 hostPool = (HostConnectionPool) _pools[key];
98 if (hostPool == null)
100 hostPool = new HostConnectionPool(host, port);
101 _pools[key] = hostPool;
105 return hostPool.GetConnection();
108 private static void ConnectionCollector ()
110 while (true)
112 Thread.Sleep(3000);
113 lock (_pools)
115 ICollection values = _pools.Values;
116 foreach (HostConnectionPool pool in values)
117 pool.PurgeConnections();
123 internal class ReusableTcpClient : TcpClient
125 public ReusableTcpClient (string host, int port): base (host, port)
127 // Avoid excessive waiting for data by the tcp stack in linux.
128 // We can't safely use SetSocketOption for both runtimes because
129 // it would break 2.0 TcpClient's property cache.
130 #if NET_2_0
131 Client.NoDelay = true;
132 #else
133 Client.SetSocketOption (SocketOptionLevel.Tcp,
134 SocketOptionName.NoDelay, 1);
135 #endif
138 public bool IsAlive
142 // This Poll will return true if there is data pending to
143 // be read. It prob. means that a client object using this
144 // connection got an exception and did not finish to read
145 // the data. It can also mean that the connection has been
146 // closed in the server. In both cases, the connection cannot
147 // be reused.
148 return !Client.Poll (0, SelectMode.SelectRead);
153 internal class TcpConnection
155 DateTime _controlTime;
156 Stream _stream;
157 ReusableTcpClient _client;
158 HostConnectionPool _pool;
159 byte[] _buffer;
161 public TcpConnection (HostConnectionPool pool, ReusableTcpClient client)
163 _pool = pool;
164 _client = client;
165 _stream = new BufferedStream (client.GetStream());
166 _controlTime = DateTime.Now;
167 _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];
170 public Stream Stream
172 get { return _stream; }
175 public DateTime ControlTime
177 get { return _controlTime; }
178 set { _controlTime = value; }
181 public bool IsAlive
183 get { return _client.IsAlive; }
186 // This is a "thread safe" buffer that can be used by
187 // TcpClientTransportSink to read or send data to the stream.
188 // The buffer is "thread safe" since only one thread can
189 // use a connection at a given time.
190 public byte[] Buffer
192 get { return _buffer; }
195 // Returns the connection to the pool
196 public void Release()
198 _pool.ReleaseConnection (this);
201 public void Close()
203 _client.Close();
207 internal class HostConnectionPool
209 ArrayList _pool = new ArrayList();
210 int _activeConnections = 0;
212 string _host;
213 int _port;
215 public HostConnectionPool (string host, int port)
217 _host = host;
218 _port = port;
221 public TcpConnection GetConnection ()
223 TcpConnection connection = null;
224 lock (_pool)
228 if (_pool.Count > 0)
230 // There are available connections
232 connection = (TcpConnection)_pool[_pool.Count - 1];
233 _pool.RemoveAt(_pool.Count - 1);
234 if (!connection.IsAlive) {
235 CancelConnection (connection);
236 connection = null;
237 continue;
241 if (connection == null && _activeConnections < TcpConnectionPool.MaxOpenConnections)
243 // No connections available, but the max connections
244 // has not been reached yet, so a new one can be created
245 // Create the connection outside the lock
246 break;
249 // No available connections in the pool
250 // Wait for somewone to release one.
252 if (connection == null)
254 Monitor.Wait(_pool);
257 while (connection == null);
260 if (connection == null)
261 return CreateConnection ();
262 else
263 return connection;
266 private TcpConnection CreateConnection()
270 ReusableTcpClient client = new ReusableTcpClient(_host, _port);
271 TcpConnection entry = new TcpConnection(this, client);
272 _activeConnections++;
273 return entry;
275 catch (Exception ex)
277 throw new RemotingException (ex.Message);
281 public void ReleaseConnection (TcpConnection entry)
283 lock (_pool)
285 entry.ControlTime = DateTime.Now; // Initialize timeout
286 _pool.Add (entry);
287 Monitor.Pulse (_pool);
291 private void CancelConnection(TcpConnection entry)
295 entry.Stream.Close();
296 _activeConnections--;
298 catch
303 public void PurgeConnections()
305 lock (_pool)
307 for (int n=0; n < _pool.Count; n++)
309 TcpConnection entry = (TcpConnection)_pool[n];
310 if ( (DateTime.Now - entry.ControlTime).TotalSeconds > TcpConnectionPool.KeepAliveSeconds)
312 CancelConnection (entry);
313 _pool.RemoveAt(n);
314 n--;