blob: b7346b8a358b7d95a3ebce738b4aafe53aad7565 [file] [log] [blame]
Kevin Clarkab4460d2009-03-20 02:28:41 +00001/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
Todd Lipcon53ae9f32009-12-07 00:42:38 +000018 *
19 * Contains some contributions under the Thrift Software License.
20 * Please see doc/old-thrift-license.txt in the Thrift distribution for
21 * details.
Kevin Clarkab4460d2009-03-20 02:28:41 +000022 */
23
David Reiss7f42bcf2008-01-11 20:59:12 +000024using System;
David Reiss7f42bcf2008-01-11 20:59:12 +000025using System.Threading;
26using Thrift.Protocol;
27using Thrift.Transport;
28
29namespace Thrift.Server
30{
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070031 /// <summary>
32 /// Server that uses C# built-in ThreadPool to spawn threads when handling requests
33 /// </summary>
34 public class TThreadPoolServer : TServer
35 {
Jens Geyerd1380d52017-05-12 22:49:57 +020036 private const int DEFAULT_MIN_THREADS = -1; // use .NET ThreadPool defaults
37 private const int DEFAULT_MAX_THREADS = -1; // use .NET ThreadPool defaults
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070038 private volatile bool stop = false;
David Reiss7f42bcf2008-01-11 20:59:12 +000039
Jens Geyerd1380d52017-05-12 22:49:57 +020040 public struct Configuration
41 {
42 public int MinWorkerThreads;
43 public int MaxWorkerThreads;
44 public int MinIOThreads;
45 public int MaxIOThreads;
46
47 public Configuration(int min = DEFAULT_MIN_THREADS, int max = DEFAULT_MAX_THREADS)
48 {
49 MinWorkerThreads = min;
50 MaxWorkerThreads = max;
51 MinIOThreads = min;
52 MaxIOThreads = max;
53 }
54
55 public Configuration(int minWork, int maxWork, int minIO, int maxIO)
56 {
57 MinWorkerThreads = minWork;
58 MaxWorkerThreads = maxWork;
59 MinIOThreads = minIO;
60 MaxIOThreads = maxIO;
61 }
62 }
63
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070064 public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport)
Jonathan Heard2bfd7df2015-10-28 17:34:27 +000065 : this(new TSingletonProcessorFactory(processor), serverTransport,
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070066 new TTransportFactory(), new TTransportFactory(),
67 new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
Jens Geyerd1380d52017-05-12 22:49:57 +020068 new Configuration(), DefaultLogDelegate)
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070069 {
70 }
David Reiss7f42bcf2008-01-11 20:59:12 +000071
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070072 public TThreadPoolServer(TProcessor processor, TServerTransport serverTransport, LogDelegate logDelegate)
Jonathan Heard2bfd7df2015-10-28 17:34:27 +000073 : this(new TSingletonProcessorFactory(processor), serverTransport,
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070074 new TTransportFactory(), new TTransportFactory(),
75 new TBinaryProtocol.Factory(), new TBinaryProtocol.Factory(),
Jens Geyerd1380d52017-05-12 22:49:57 +020076 new Configuration(), logDelegate)
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070077 {
78 }
David Reiss63191332009-01-06 19:49:22 +000079
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070080 public TThreadPoolServer(TProcessor processor,
Jonathan Heard2bfd7df2015-10-28 17:34:27 +000081 TServerTransport serverTransport,
82 TTransportFactory transportFactory,
83 TProtocolFactory protocolFactory)
84 : this(new TSingletonProcessorFactory(processor), serverTransport,
85 transportFactory, transportFactory,
86 protocolFactory, protocolFactory,
Jens Geyerd1380d52017-05-12 22:49:57 +020087 new Configuration(), DefaultLogDelegate)
Jonathan Heard2bfd7df2015-10-28 17:34:27 +000088 {
89 }
90
91 public TThreadPoolServer(TProcessorFactory processorFactory,
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070092 TServerTransport serverTransport,
93 TTransportFactory transportFactory,
94 TProtocolFactory protocolFactory)
Jonathan Heard2bfd7df2015-10-28 17:34:27 +000095 : this(processorFactory, serverTransport,
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070096 transportFactory, transportFactory,
97 protocolFactory, protocolFactory,
Jens Geyerd1380d52017-05-12 22:49:57 +020098 new Configuration(), DefaultLogDelegate)
Randy Abernethy0e86f1f2014-07-13 09:50:19 -070099 {
100 }
David Reiss7f42bcf2008-01-11 20:59:12 +0000101
Jonathan Heard2bfd7df2015-10-28 17:34:27 +0000102 public TThreadPoolServer(TProcessorFactory processorFactory,
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700103 TServerTransport serverTransport,
104 TTransportFactory inputTransportFactory,
105 TTransportFactory outputTransportFactory,
106 TProtocolFactory inputProtocolFactory,
107 TProtocolFactory outputProtocolFactory,
108 int minThreadPoolThreads, int maxThreadPoolThreads, LogDelegate logDel)
Jens Geyerd1380d52017-05-12 22:49:57 +0200109 : this(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
110 inputProtocolFactory, outputProtocolFactory,
111 new Configuration(minThreadPoolThreads, maxThreadPoolThreads),
112 logDel)
113 {
114 }
115
116 public TThreadPoolServer(TProcessorFactory processorFactory,
117 TServerTransport serverTransport,
118 TTransportFactory inputTransportFactory,
119 TTransportFactory outputTransportFactory,
120 TProtocolFactory inputProtocolFactory,
121 TProtocolFactory outputProtocolFactory,
122 Configuration threadConfig,
123 LogDelegate logDel)
Jonathan Heard2bfd7df2015-10-28 17:34:27 +0000124 : base(processorFactory, serverTransport, inputTransportFactory, outputTransportFactory,
Jens Geyerd1380d52017-05-12 22:49:57 +0200125 inputProtocolFactory, outputProtocolFactory, logDel)
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700126 {
127 lock (typeof(TThreadPoolServer))
128 {
Jens Geyerd1380d52017-05-12 22:49:57 +0200129 if ((threadConfig.MaxWorkerThreads > 0) || (threadConfig.MaxIOThreads > 0))
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700130 {
Jens Geyerd1380d52017-05-12 22:49:57 +0200131 int work, comm;
132 ThreadPool.GetMaxThreads(out work, out comm);
133 if (threadConfig.MaxWorkerThreads > 0)
134 work = threadConfig.MaxWorkerThreads;
135 if (threadConfig.MaxIOThreads > 0)
136 comm = threadConfig.MaxIOThreads;
137 if (!ThreadPool.SetMaxThreads(work, comm))
Jens Geyer224c3342017-05-06 22:38:43 +0200138 throw new Exception("Error: could not SetMaxThreads in ThreadPool");
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700139 }
Jens Geyerd1380d52017-05-12 22:49:57 +0200140
141 if ((threadConfig.MinWorkerThreads > 0) || (threadConfig.MinIOThreads > 0))
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700142 {
Jens Geyerd1380d52017-05-12 22:49:57 +0200143 int work, comm;
144 ThreadPool.GetMinThreads(out work, out comm);
145 if (threadConfig.MinWorkerThreads > 0)
146 work = threadConfig.MinWorkerThreads;
147 if (threadConfig.MinIOThreads > 0)
148 comm = threadConfig.MinIOThreads;
149 if (!ThreadPool.SetMinThreads(work, comm))
Jens Geyer224c3342017-05-06 22:38:43 +0200150 throw new Exception("Error: could not SetMinThreads in ThreadPool");
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700151 }
152 }
153 }
David Reiss7f42bcf2008-01-11 20:59:12 +0000154
Roger Meierb1ec4cc2012-04-11 21:21:41 +0000155
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700156 /// <summary>
157 /// Use new ThreadPool thread for each new client connection
158 /// </summary>
159 public override void Serve()
160 {
161 try
162 {
163 serverTransport.Listen();
164 }
165 catch (TTransportException ttx)
166 {
167 logDelegate("Error, could not listen on ServerTransport: " + ttx);
168 return;
169 }
David Reiss7f42bcf2008-01-11 20:59:12 +0000170
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700171 //Fire the preServe server event when server is up but before any client connections
172 if (serverEventHandler != null)
173 serverEventHandler.preServe();
David Reiss63191332009-01-06 19:49:22 +0000174
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700175 while (!stop)
176 {
177 int failureCount = 0;
178 try
179 {
180 TTransport client = serverTransport.Accept();
181 ThreadPool.QueueUserWorkItem(this.Execute, client);
182 }
183 catch (TTransportException ttx)
184 {
Jens Geyer7d882082015-01-27 22:08:44 +0100185 if (!stop || ttx.Type != TTransportException.ExceptionType.Interrupted)
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700186 {
187 ++failureCount;
188 logDelegate(ttx.ToString());
189 }
David Reissdc815f52008-03-02 00:58:04 +0000190
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700191 }
192 }
David Reissdc815f52008-03-02 00:58:04 +0000193
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700194 if (stop)
195 {
196 try
197 {
198 serverTransport.Close();
199 }
200 catch (TTransportException ttx)
201 {
202 logDelegate("TServerTransport failed on close: " + ttx.Message);
203 }
204 stop = false;
205 }
206 }
David Reiss7f42bcf2008-01-11 20:59:12 +0000207
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700208 /// <summary>
209 /// Loops on processing a client forever
210 /// threadContext will be a TTransport instance
211 /// </summary>
212 /// <param name="threadContext"></param>
213 private void Execute(Object threadContext)
214 {
215 TTransport client = (TTransport)threadContext;
Jonathan Heard2bfd7df2015-10-28 17:34:27 +0000216 TProcessor processor = processorFactory.GetProcessor(client, this);
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700217 TTransport inputTransport = null;
218 TTransport outputTransport = null;
219 TProtocol inputProtocol = null;
220 TProtocol outputProtocol = null;
221 Object connectionContext = null;
222 try
223 {
224 inputTransport = inputTransportFactory.GetTransport(client);
225 outputTransport = outputTransportFactory.GetTransport(client);
226 inputProtocol = inputProtocolFactory.GetProtocol(inputTransport);
227 outputProtocol = outputProtocolFactory.GetProtocol(outputTransport);
David Reiss63191332009-01-06 19:49:22 +0000228
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700229 //Recover event handler (if any) and fire createContext server event when a client connects
230 if (serverEventHandler != null)
231 connectionContext = serverEventHandler.createContext(inputProtocol, outputProtocol);
232
233 //Process client requests until client disconnects
Jens Geyer7d882082015-01-27 22:08:44 +0100234 while (!stop)
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700235 {
Jens Geyerd5436f52014-10-03 19:50:38 +0200236 if (!inputTransport.Peek())
Jens Geyereb8e5ad2014-09-29 21:50:15 +0200237 break;
238
Randy Abernethy0e86f1f2014-07-13 09:50:19 -0700239 //Fire processContext server event
240 //N.B. This is the pattern implemented in C++ and the event fires provisionally.
241 //That is to say it may be many minutes between the event firing and the client request
242 //actually arriving or the client may hang up without ever makeing a request.
243 if (serverEventHandler != null)
244 serverEventHandler.processContext(connectionContext, inputTransport);
245 //Process client request (blocks until transport is readable)
246 if (!processor.Process(inputProtocol, outputProtocol))
247 break;
248 }
249 }
250 catch (TTransportException)
251 {
252 //Usually a client disconnect, expected
253 }
254 catch (Exception x)
255 {
256 //Unexpected
257 logDelegate("Error: " + x);
258 }
259
260 //Fire deleteContext server event after client disconnects
261 if (serverEventHandler != null)
262 serverEventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol);
263
264 //Close transports
265 if (inputTransport != null)
266 inputTransport.Close();
267 if (outputTransport != null)
268 outputTransport.Close();
269 }
270
271 public override void Stop()
272 {
273 stop = true;
274 serverTransport.Close();
275 }
276 }
David Reiss7f42bcf2008-01-11 20:59:12 +0000277}