2 // System.Runtime.Remoting.Channels.Tcp.TcpConnectionPool.cs
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)
6 // 2002 (C) Lluis Sanchez Gual
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
31 using System
.Collections
;
32 using System
.Threading
;
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;
66 public static void Shutdown ()
69 if (_poolThread
!= null)
74 public static int MaxOpenConnections
76 get { return _maxOpenConnections; }
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
;
96 string key
= host
+ ":" + port
;
97 hostPool
= (HostConnectionPool
) _pools
[key
];
100 hostPool
= new HostConnectionPool(host
, port
);
101 _pools
[key
] = hostPool
;
105 return hostPool
.GetConnection();
108 private static void ConnectionCollector ()
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.
131 Client
.NoDelay
= true;
133 Client
.SetSocketOption (SocketOptionLevel
.Tcp
,
134 SocketOptionName
.NoDelay
, 1);
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
148 return !Client
.Poll (0, SelectMode
.SelectRead
);
153 internal class TcpConnection
155 DateTime _controlTime
;
157 ReusableTcpClient _client
;
158 HostConnectionPool _pool
;
161 public TcpConnection (HostConnectionPool pool
, ReusableTcpClient client
)
165 _stream
= new BufferedStream (client
.GetStream());
166 _controlTime
= DateTime
.Now
;
167 _buffer
= new byte[TcpMessageIO
.DefaultStreamBufferSize
];
172 get { return _stream; }
175 public DateTime ControlTime
177 get { return _controlTime; }
178 set { _controlTime = value; }
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.
192 get { return _buffer; }
195 // Returns the connection to the pool
196 public void Release()
198 _pool
.ReleaseConnection (this);
207 internal class HostConnectionPool
209 ArrayList _pool
= new ArrayList();
210 int _activeConnections
= 0;
215 public HostConnectionPool (string host
, int port
)
221 public TcpConnection
GetConnection ()
223 TcpConnection connection
= null;
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
);
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
249 // No available connections in the pool
250 // Wait for somewone to release one.
252 if (connection
== null)
257 while (connection
== null);
260 if (connection
== null)
261 return CreateConnection ();
266 private TcpConnection
CreateConnection()
270 ReusableTcpClient client
= new ReusableTcpClient(_host
, _port
);
271 TcpConnection entry
= new TcpConnection(this, client
);
272 _activeConnections
++;
277 throw new RemotingException (ex
.Message
);
281 public void ReleaseConnection (TcpConnection entry
)
285 entry
.ControlTime
= DateTime
.Now
; // Initialize timeout
287 Monitor
.Pulse (_pool
);
291 private void CancelConnection(TcpConnection entry
)
295 entry
.Stream
.Close();
296 _activeConnections
--;
303 public void PurgeConnections()
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
);