cb2809377a0eb7e877433b5e8a878a284d015fae
[mono.git] / mcs / class / Mono.Posix / Mono.Remoting.Channels.Unix / UnixConnectionPool.cs
1 //\r
2 // Mono.Remoting.Channels.Unix.UnixConnectionPool.cs\r
3 //\r
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)\r
5 //\r
6 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)\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 using System.Runtime.Remoting;\r
36 using Mono.Unix;\r
37 \r
38 namespace Mono.Remoting.Channels.Unix\r
39 {\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
48 \r
49         internal class UnixConnectionPool\r
50         {\r
51                 // Table of pools. There is a HostConnectionPool \r
52                 // instance for each host\r
53                 static Hashtable _pools = new Hashtable();\r
54 \r
55                 static int _maxOpenConnections = int.MaxValue;\r
56                 static int _keepAliveSeconds = 15;\r
57 \r
58                 static Thread _poolThread;\r
59 \r
60                 static UnixConnectionPool()\r
61                 {\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
66                 }\r
67 \r
68                 public static void Shutdown ()\r
69                 {\r
70                         if (_poolThread != null)\r
71                                 _poolThread.Abort();\r
72                 }\r
73 \r
74                 public static int MaxOpenConnections\r
75                 {\r
76                         get { return _maxOpenConnections; }\r
77                         set \r
78                         { \r
79                                 if (value < 1) throw new RemotingException ("MaxOpenConnections must be greater than zero");\r
80                                 _maxOpenConnections = value; \r
81                         }\r
82                 }\r
83 \r
84                 public static int KeepAliveSeconds\r
85                 {\r
86                         get { return _keepAliveSeconds; }\r
87                         set { _keepAliveSeconds = value; }\r
88                 }\r
89 \r
90                 public static UnixConnection GetConnection (string path)\r
91                 {\r
92                         HostConnectionPool hostPool;\r
93 \r
94                         lock (_pools)\r
95                         {\r
96                                 hostPool = (HostConnectionPool) _pools[path];\r
97                                 if (hostPool == null)\r
98                                 {\r
99                                         hostPool = new HostConnectionPool(path);\r
100                                         _pools[path] = hostPool;\r
101                                 }\r
102                         }\r
103 \r
104                         return hostPool.GetConnection();\r
105                 }\r
106 \r
107                 private static void ConnectionCollector ()\r
108                 {\r
109                         while (true)\r
110                         {\r
111                                 Thread.Sleep(3000);\r
112                                 lock (_pools)\r
113                                 {\r
114                                         ICollection values = _pools.Values;\r
115                                         foreach (HostConnectionPool pool in values)\r
116                                                 pool.PurgeConnections();\r
117                                 }\r
118                         }\r
119                 }\r
120         }\r
121 \r
122         internal class ReusableUnixClient : UnixClient\r
123         {\r
124                 public ReusableUnixClient (string path): base (path)\r
125                 {\r
126                 }\r
127                 \r
128                 public bool IsAlive\r
129                 {\r
130                         get\r
131                         {\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
137                                 // be reused.\r
138                                 return !Client.Poll (0, SelectMode.SelectRead);\r
139                         }\r
140                 }\r
141         }\r
142 \r
143         internal class UnixConnection\r
144         {\r
145                 DateTime _controlTime;\r
146                 Stream _stream;\r
147                 ReusableUnixClient _client;\r
148                 HostConnectionPool _pool;\r
149                 byte[] _buffer;\r
150 \r
151                 public UnixConnection (HostConnectionPool pool, ReusableUnixClient client)\r
152                 {\r
153                         _pool = pool;\r
154                         _client = client;\r
155                         _stream = new BufferedStream (client.GetStream());\r
156                         _controlTime = DateTime.Now;\r
157                         _buffer = new byte[UnixMessageIO.DefaultStreamBufferSize];\r
158                 }\r
159 \r
160                 public Stream Stream\r
161                 {\r
162                         get { return _stream; }\r
163                 }\r
164 \r
165                 public DateTime ControlTime\r
166                 {\r
167                         get { return _controlTime; }\r
168                         set { _controlTime = value; }\r
169                 }\r
170 \r
171                 public bool IsAlive\r
172                 {\r
173                         get { return _client.IsAlive; }\r
174                 }\r
175 \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
181                 {\r
182                         get { return _buffer; }\r
183                 }\r
184 \r
185                 // Returns the connection to the pool\r
186                 public void Release()\r
187                 {\r
188                         _pool.ReleaseConnection (this);\r
189                 }\r
190 \r
191                 public void Close()\r
192                 {\r
193                         _client.Close();\r
194                 }\r
195         }\r
196 \r
197         internal class HostConnectionPool\r
198         {\r
199                 ArrayList _pool = new ArrayList();\r
200                 int _activeConnections = 0;\r
201 \r
202                 string _path;\r
203 \r
204                 public HostConnectionPool (string path)\r
205                 {\r
206                         _path = path;\r
207                 }\r
208 \r
209                 public UnixConnection GetConnection ()\r
210                 {\r
211                         UnixConnection connection = null;\r
212                         lock (_pool)\r
213                         {\r
214                                 do\r
215                                 {\r
216                                         if (_pool.Count > 0) \r
217                                         {\r
218                                                 // There are available connections\r
219 \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
224                                                         connection = null;\r
225                                                         continue;\r
226                                                 }\r
227                                         }\r
228 \r
229                                         if (connection == null && _activeConnections < UnixConnectionPool.MaxOpenConnections)\r
230                                         {\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
234                                                 break;\r
235                                         }\r
236 \r
237                                         // No available connections in the pool\r
238                                         // Wait for somewone to release one.\r
239 \r
240                                         if (connection == null)\r
241                                         {\r
242                                                 Monitor.Wait(_pool);\r
243                                         }\r
244                                 } \r
245                                 while (connection == null);\r
246                         }\r
247 \r
248                         if (connection == null)\r
249                                 return CreateConnection ();\r
250                         else\r
251                                 return connection;\r
252                 }\r
253 \r
254                 private UnixConnection CreateConnection()\r
255                 {\r
256                         try\r
257                         {\r
258                                 ReusableUnixClient client = new ReusableUnixClient (_path);\r
259                                 UnixConnection entry = new UnixConnection(this, client);\r
260                                 _activeConnections++;\r
261                                 return entry;\r
262                         }\r
263                         catch (Exception ex)\r
264                         {\r
265                                 throw new RemotingException (ex.Message);\r
266                         }\r
267                 }\r
268 \r
269                 public void ReleaseConnection (UnixConnection entry)\r
270                 {\r
271                         lock (_pool)\r
272                         {\r
273                                 entry.ControlTime = DateTime.Now;       // Initialize timeout\r
274                                 _pool.Add (entry);\r
275                                 Monitor.Pulse (_pool);\r
276                         }\r
277                 }\r
278 \r
279                 private void CancelConnection(UnixConnection entry)\r
280                 {\r
281                         try\r
282                         {\r
283                                 entry.Stream.Close();\r
284                                 _activeConnections--;\r
285                         }\r
286                         catch\r
287                         {\r
288                         }\r
289                 }\r
290 \r
291                 public void PurgeConnections()\r
292                 {\r
293                         lock (_pool)\r
294                         {\r
295                                 for (int n=0; n < _pool.Count; n++)\r
296                                 {\r
297                                         UnixConnection entry = (UnixConnection)_pool[n];\r
298                                         if ( (DateTime.Now - entry.ControlTime).TotalSeconds > UnixConnectionPool.KeepAliveSeconds)\r
299                                         {\r
300                                                 CancelConnection (entry);\r
301                                                 _pool.RemoveAt(n);\r
302                                                 n--;\r
303                                         }\r
304                                 }\r
305                         }\r
306                 }\r
307 \r
308         }\r
309 \r
310 \r
311 }\r