[bcl] Remove NET_2_0 defines from the class libs. This has been done using: unifdef...
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Tcp / TcpConnectionPool.cs
1 //\r
2 // System.Runtime.Remoting.Channels.Tcp.TcpConnectionPool.cs\r
3 //\r
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)\r
5 //\r
6 // 2002 (C) Lluis Sanchez Gual\r
7 //\r
8 \r
9 //\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
17 // \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
20 // \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
28 //\r
29 \r
30 using System;\r
31 using System.Collections;\r
32 using System.Threading;\r
33 using System.IO;\r
34 using System.Net.Sockets;\r
35 \r
36 namespace System.Runtime.Remoting.Channels.Tcp\r
37 {\r
38         // This is a pool of Tcp connections. Connections requested\r
39         // by the TCP channel are pooled after their use, and can\r
40         // be reused later. Connections are automaticaly closed\r
41         // if not used after some time, specified in KeepAliveSeconds.\r
42         // The number of allowed open connections can also be specified\r
43         // in MaxOpenConnections. The limit is per host.\r
44         // If a thread requests a connection and the limit has been \r
45         // reached, the thread is suspended until one is released.\r
46 \r
47         internal class TcpConnectionPool\r
48         {\r
49                 // Table of pools. There is a HostConnectionPool \r
50                 // instance for each host\r
51                 static Hashtable _pools = new Hashtable();\r
52 \r
53                 static int _maxOpenConnections = int.MaxValue;\r
54                 static int _keepAliveSeconds = 15;\r
55 \r
56                 static Thread _poolThread;\r
57 \r
58                 static TcpConnectionPool()\r
59                 {\r
60                         // This thread will close unused connections\r
61                         _poolThread = new Thread (new ThreadStart (ConnectionCollector));\r
62                         _poolThread.IsBackground = true;\r
63                         _poolThread.Start();\r
64                 }\r
65 \r
66                 public static void Shutdown ()\r
67                 {\r
68                         if (_poolThread != null)\r
69                                 _poolThread.Abort();\r
70                 }\r
71 \r
72                 public static int MaxOpenConnections\r
73                 {\r
74                         get { return _maxOpenConnections; }\r
75                         set \r
76                         { \r
77                                 if (value < 1) throw new RemotingException ("MaxOpenConnections must be greater than zero");\r
78                                 _maxOpenConnections = value; \r
79                         }\r
80                 }\r
81 \r
82                 public static int KeepAliveSeconds\r
83                 {\r
84                         get { return _keepAliveSeconds; }\r
85                         set { _keepAliveSeconds = value; }\r
86                 }\r
87 \r
88                 public static TcpConnection GetConnection (string host, int port)\r
89                 {\r
90                         HostConnectionPool hostPool;\r
91 \r
92                         lock (_pools)\r
93                         {\r
94                                 string key = host + ":" + port;\r
95                                 hostPool = (HostConnectionPool) _pools[key];\r
96                                 if (hostPool == null)\r
97                                 {\r
98                                         hostPool = new HostConnectionPool(host, port);\r
99                                         _pools[key] = hostPool;\r
100                                 }\r
101                         }\r
102 \r
103                         return hostPool.GetConnection();\r
104                 }\r
105 \r
106                 private static void ConnectionCollector ()\r
107                 {\r
108                         while (true)\r
109                         {\r
110                                 Thread.Sleep(3000);\r
111                                 lock (_pools)\r
112                                 {\r
113                                         ICollection values = _pools.Values;\r
114                                         foreach (HostConnectionPool pool in values)\r
115                                                 pool.PurgeConnections();\r
116                                 }\r
117                         }\r
118                 }\r
119         }\r
120 \r
121         internal class ReusableTcpClient : TcpClient\r
122         {\r
123                 public ReusableTcpClient (string host, int port): base (host, port)\r
124                 {\r
125                         // Avoid excessive waiting for data by the tcp stack in linux.\r
126                         // We can't safely use SetSocketOption for both runtimes because\r
127                         // it would break 2.0 TcpClient's property cache.\r
128                         Client.NoDelay = true;\r
129                 }\r
130                 \r
131                 public bool IsAlive\r
132                 {\r
133                         get\r
134                         {\r
135                                 // This Poll will return true if there is data pending to\r
136                                 // be read. It prob. means that a client object using this\r
137                                 // connection got an exception and did not finish to read\r
138                                 // the data. It can also mean that the connection has been\r
139                                 // closed in the server. In both cases, the connection cannot\r
140                                 // be reused.\r
141                                 return !Client.Poll (0, SelectMode.SelectRead);\r
142                         }\r
143                 }\r
144         }\r
145 \r
146         internal class TcpConnection\r
147         {\r
148                 DateTime _controlTime;\r
149                 Stream _stream;\r
150                 ReusableTcpClient _client;\r
151                 HostConnectionPool _pool;\r
152                 byte[] _buffer;\r
153 \r
154                 public TcpConnection (HostConnectionPool pool, ReusableTcpClient client)\r
155                 {\r
156                         _pool = pool;\r
157                         _client = client;\r
158                         _stream = new BufferedStream (client.GetStream());\r
159                         _controlTime = DateTime.Now;\r
160                         _buffer = new byte[TcpMessageIO.DefaultStreamBufferSize];\r
161                 }\r
162 \r
163                 public Stream Stream\r
164                 {\r
165                         get { return _stream; }\r
166                 }\r
167 \r
168                 public DateTime ControlTime\r
169                 {\r
170                         get { return _controlTime; }\r
171                         set { _controlTime = value; }\r
172                 }\r
173 \r
174                 public bool IsAlive\r
175                 {\r
176                         get { return _client.IsAlive; }\r
177                 }\r
178 \r
179                 // This is a "thread safe" buffer that can be used by \r
180                 // TcpClientTransportSink to read or send data to the stream.\r
181                 // The buffer is "thread safe" since only one thread can\r
182                 // use a connection at a given time.\r
183                 public byte[] Buffer\r
184                 {\r
185                         get { return _buffer; }\r
186                 }\r
187 \r
188                 // Returns the connection to the pool\r
189                 public void Release()\r
190                 {\r
191                         _pool.ReleaseConnection (this);\r
192                 }\r
193 \r
194                 public void Close()\r
195                 {\r
196                         _client.Close();\r
197                 }\r
198         }\r
199 \r
200         internal class HostConnectionPool\r
201         {\r
202                 ArrayList _pool = new ArrayList();\r
203                 int _activeConnections = 0;\r
204 \r
205                 string _host;\r
206                 int _port;\r
207 \r
208                 public HostConnectionPool (string host, int port)\r
209                 {\r
210                         _host = host;\r
211                         _port = port;\r
212                 }\r
213 \r
214                 public TcpConnection GetConnection ()\r
215                 {\r
216                         TcpConnection connection = null;\r
217                         lock (_pool)\r
218                         {\r
219                                 do\r
220                                 {\r
221                                         if (_pool.Count > 0) \r
222                                         {\r
223                                                 // There are available connections\r
224 \r
225                                                 connection = (TcpConnection)_pool[_pool.Count - 1];\r
226                                                 _pool.RemoveAt(_pool.Count - 1);\r
227                                                 if (!connection.IsAlive) {\r
228                                                         CancelConnection (connection);\r
229                                                         connection = null;\r
230                                                         continue;\r
231                                                 }\r
232                                         }\r
233 \r
234                                         if (connection == null && _activeConnections < TcpConnectionPool.MaxOpenConnections)\r
235                                         {\r
236                                                 // No connections available, but the max connections\r
237                                                 // has not been reached yet, so a new one can be created\r
238                                                 // Create the connection outside the lock\r
239                                                 break;\r
240                                         }\r
241 \r
242                                         // No available connections in the pool\r
243                                         // Wait for somewone to release one.\r
244 \r
245                                         if (connection == null)\r
246                                         {\r
247                                                 Monitor.Wait(_pool);\r
248                                         }\r
249                                 } \r
250                                 while (connection == null);\r
251                         }\r
252 \r
253                         if (connection == null)\r
254                                 return CreateConnection ();\r
255                         else\r
256                                 return connection;\r
257                 }\r
258 \r
259                 private TcpConnection CreateConnection()\r
260                 {\r
261                         try\r
262                         {\r
263                                 ReusableTcpClient client = new ReusableTcpClient(_host, _port);\r
264                                 TcpConnection entry = new TcpConnection(this, client);\r
265                                 _activeConnections++;\r
266                                 return entry;\r
267                         }\r
268                         catch (Exception ex)\r
269                         {\r
270                                 throw new RemotingException (ex.Message);\r
271                         }\r
272                 }\r
273 \r
274                 public void ReleaseConnection (TcpConnection entry)\r
275                 {\r
276                         lock (_pool)\r
277                         {\r
278                                 entry.ControlTime = DateTime.Now;       // Initialize timeout\r
279                                 _pool.Add (entry);\r
280                                 Monitor.Pulse (_pool);\r
281                         }\r
282                 }\r
283 \r
284                 private void CancelConnection(TcpConnection entry)\r
285                 {\r
286                         try\r
287                         {\r
288                                 entry.Stream.Close();\r
289                                 _activeConnections--;\r
290                         }\r
291                         catch\r
292                         {\r
293                         }\r
294                 }\r
295 \r
296                 public void PurgeConnections()\r
297                 {\r
298                         lock (_pool)\r
299                         {\r
300                                 for (int n=0; n < _pool.Count; n++)\r
301                                 {\r
302                                         TcpConnection entry = (TcpConnection)_pool[n];\r
303                                         if ( (DateTime.Now - entry.ControlTime).TotalSeconds > TcpConnectionPool.KeepAliveSeconds)\r
304                                         {\r
305                                                 CancelConnection (entry);\r
306                                                 _pool.RemoveAt(n);\r
307                                                 n--;\r
308                                         }\r
309                                 }\r
310                         }\r
311                 }\r
312 \r
313         }\r
314 \r
315 \r
316 }\r