PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/cxxtools-2.1.1/src/bin/rpcserverimpl.cpp

#
C++ | 342 lines | 245 code | 61 blank | 36 comment | 23 complexity | ad7e9ceec332584b8b182b8365193cf3 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * Copyright (C) 2011 Tommi Maekitalo
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * As a special exception, you may use this file as part of a free
  10. * software library without restriction. Specifically, if other files
  11. * instantiate templates or use macros or inline functions from this
  12. * file, or you compile this file and link it with other files to
  13. * produce an executable, this file does not by itself cause the
  14. * resulting executable to be covered by the GNU General Public
  15. * License. This exception does not however invalidate any other
  16. * reasons why the executable file might be covered by the GNU Library
  17. * General Public License.
  18. *
  19. * This library is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  22. * Lesser General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU Lesser General Public
  25. * License along with this library; if not, write to the Free Software
  26. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  27. */
  28. #include "rpcserverimpl.h"
  29. #include "listener.h"
  30. #include "socket.h"
  31. #include "worker.h"
  32. #include <cxxtools/eventloop.h>
  33. #include <cxxtools/net/tcpserver.h>
  34. #include <cxxtools/log.h>
  35. #include <signal.h>
  36. log_define("cxxtools.bin.rpcserver.impl")
  37. namespace cxxtools
  38. {
  39. namespace bin
  40. {
  41. // Sent from the worker thread when a socket is idle.
  42. // The server will take that socket to the event loop.
  43. class IdleSocketEvent : public BasicEvent<IdleSocketEvent>
  44. {
  45. Socket* _socket;
  46. public:
  47. explicit IdleSocketEvent(Socket* socket)
  48. : _socket(socket)
  49. { }
  50. Socket* socket() const { return _socket; }
  51. };
  52. // Sent from the server when constructed, so that the server
  53. // knows, when the event loop is running.
  54. class ServerStartEvent : public BasicEvent<ServerStartEvent>
  55. {
  56. const RpcServerImpl* _server;
  57. public:
  58. explicit ServerStartEvent(const RpcServerImpl* server)
  59. : _server(server)
  60. { }
  61. const RpcServerImpl* server() const { return _server; }
  62. };
  63. // Sent from a worker, when a job was fetched from the queue and
  64. // no further threads are left for subsequent jobs.
  65. class NoWaitingThreadsEvent : public BasicEvent<NoWaitingThreadsEvent>
  66. {
  67. };
  68. // Sent from the worker, when he decidid to stop, because there are
  69. // enough idle threads waiting on the queue already.
  70. class ThreadTerminatedEvent : public BasicEvent<ThreadTerminatedEvent>
  71. {
  72. Worker* _worker;
  73. public:
  74. explicit ThreadTerminatedEvent(Worker* worker)
  75. : _worker(worker)
  76. { }
  77. Worker* worker() const { return _worker; }
  78. };
  79. RpcServerImpl::RpcServerImpl(EventLoopBase& eventLoop, Signal<RpcServer::Runmode>& runmodeChanged, ServiceRegistry& serviceRegistry)
  80. : _runmode(RpcServer::Stopped),
  81. _runmodeChanged(runmodeChanged),
  82. _eventLoop(eventLoop),
  83. inputSlot(slot(*this, &RpcServerImpl::onInput)),
  84. _serviceRegistry(serviceRegistry),
  85. _minThreads(5),
  86. _maxThreads(200)
  87. {
  88. _eventLoop.event.subscribe(slot(*this, &RpcServerImpl::onIdleSocket));
  89. _eventLoop.event.subscribe(slot(*this, &RpcServerImpl::onNoWaitingThreads));
  90. _eventLoop.event.subscribe(slot(*this, &RpcServerImpl::onThreadTerminated));
  91. _eventLoop.event.subscribe(slot(*this, &RpcServerImpl::onServerStart));
  92. connect(_eventLoop.exited, *this, &RpcServerImpl::terminate);
  93. _eventLoop.commitEvent(ServerStartEvent(this));
  94. }
  95. RpcServerImpl::~RpcServerImpl()
  96. {
  97. if (runmode() == RpcServer::Running)
  98. {
  99. try
  100. {
  101. terminate();
  102. }
  103. catch (const std::exception& e)
  104. {
  105. log_fatal("failed to terminate rpc server: " << e.what());
  106. }
  107. }
  108. }
  109. void RpcServerImpl::listen(const std::string& ip, unsigned short int port, int backlog)
  110. {
  111. log_debug("listen on " << ip << " port " << port);
  112. Listener* listener = new Listener(ip, port, backlog, net::TcpServer::DEFER_ACCEPT);
  113. try
  114. {
  115. _listener.push_back(listener);
  116. _queue.put(new Socket(*this, _serviceRegistry, *listener));
  117. }
  118. catch (...)
  119. {
  120. delete listener;
  121. throw;
  122. }
  123. }
  124. void RpcServerImpl::start()
  125. {
  126. log_trace("start server");
  127. runmode(RpcServer::Starting);
  128. // SIGPIPE must be ignored
  129. ::signal(SIGPIPE, SIG_IGN);
  130. MutexLock lock(_threadMutex);
  131. while (_threads.size() < minThreads())
  132. {
  133. Worker* worker = new Worker(*this);
  134. _threads.insert(worker);
  135. worker->start();
  136. }
  137. runmode(RpcServer::Running);
  138. }
  139. void RpcServerImpl::terminate()
  140. {
  141. MutexLock lock(_threadMutex);
  142. runmode(RpcServer::Terminating);
  143. try
  144. {
  145. for (unsigned n = 0; n < _listener.size(); ++n)
  146. _listener[n]->wakeConnect();
  147. _queue.put(0);
  148. while (!_threads.empty() || !_terminatedThreads.empty())
  149. {
  150. if (!_threads.empty())
  151. {
  152. _threadTerminated.wait(lock);
  153. }
  154. for (Threads::iterator it = _terminatedThreads.begin(); it != _terminatedThreads.end(); ++it)
  155. delete *it;
  156. _terminatedThreads.clear();
  157. }
  158. for (unsigned n = 0; n < _listener.size(); ++n)
  159. delete _listener[n];
  160. _listener.clear();
  161. while (!_queue.empty())
  162. delete _queue.get();
  163. for (IdleSocket::iterator it = _idleSocket.begin(); it != _idleSocket.end(); ++it)
  164. delete *it;
  165. _idleSocket.clear();
  166. runmode(RpcServer::Stopped);
  167. }
  168. catch (const std::exception& e)
  169. {
  170. runmode(RpcServer::Failed);
  171. }
  172. }
  173. void RpcServerImpl::noWaitingThreads()
  174. {
  175. if (runmode() == RpcServer::Running)
  176. _eventLoop.commitEvent(NoWaitingThreadsEvent());
  177. }
  178. void RpcServerImpl::threadTerminated(Worker* worker)
  179. {
  180. MutexLock lock(_threadMutex);
  181. _threads.erase(worker);
  182. if (runmode() == RpcServer::Running)
  183. {
  184. _eventLoop.commitEvent(ThreadTerminatedEvent(worker));
  185. }
  186. else
  187. {
  188. _terminatedThreads.insert(worker);
  189. _threadTerminated.signal();
  190. }
  191. }
  192. void RpcServerImpl::addIdleSocket(Socket* socket)
  193. {
  194. log_debug("add idle socket " << static_cast<void*>(socket));
  195. if (runmode() == RpcServer::Running)
  196. {
  197. _eventLoop.commitEvent(IdleSocketEvent(socket));
  198. }
  199. else
  200. {
  201. log_debug("server not running; delete " << static_cast<void*>(socket));
  202. delete socket;
  203. }
  204. }
  205. void RpcServerImpl::onIdleSocket(const IdleSocketEvent& event)
  206. {
  207. Socket* socket = event.socket();
  208. log_debug("add idle socket " << static_cast<void*>(socket) << " to selector");
  209. _idleSocket.insert(socket);
  210. socket->setSelector(&_eventLoop);
  211. socket->inputConnection = connect(socket->inputReady, inputSlot);
  212. }
  213. void RpcServerImpl::onNoWaitingThreads(const NoWaitingThreadsEvent& event)
  214. {
  215. MutexLock lock(_threadMutex);
  216. if (_threads.size() >= maxThreads())
  217. {
  218. log_warn("thread limit " << maxThreads() << " reached");
  219. return;
  220. }
  221. try
  222. {
  223. Worker* worker = new Worker(*this);
  224. try
  225. {
  226. log_debug("create thread " << static_cast<void*>(worker) << "; running threads=" << _threads.size());
  227. worker->start();
  228. _threads.insert(worker);
  229. log_debug(_threads.size() << " threads running");
  230. }
  231. catch (const std::exception&)
  232. {
  233. delete worker;
  234. throw;
  235. }
  236. }
  237. catch (const std::exception& e)
  238. {
  239. log_warn("failed to create thread: " << e.what());
  240. }
  241. }
  242. void RpcServerImpl::onThreadTerminated(const ThreadTerminatedEvent& event)
  243. {
  244. MutexLock lock(_threadMutex);
  245. log_debug("thread terminated (" << static_cast<void*>(event.worker()) << ") " << _threads.size() << " threads left");
  246. try
  247. {
  248. event.worker()->join();
  249. }
  250. catch (const std::exception& e)
  251. {
  252. log_error("failed to join thread: " << e.what());
  253. }
  254. delete event.worker();
  255. }
  256. void RpcServerImpl::onServerStart(const ServerStartEvent& event)
  257. {
  258. if (event.server() == this)
  259. {
  260. start();
  261. }
  262. }
  263. void RpcServerImpl::onInput(Socket& socket)
  264. {
  265. socket.removeSelector();
  266. log_debug("search socket " << static_cast<void*>(&socket) << " in idle socket");
  267. _idleSocket.erase(&socket);
  268. if (socket.isConnected())
  269. {
  270. socket.inputConnection.close();
  271. _queue.put(&socket);
  272. }
  273. else
  274. {
  275. log_debug("onInput; delete " << static_cast<void*>(&socket));
  276. log_info("client " << socket.getPeerAddr() << " closed connection");
  277. delete &socket;
  278. }
  279. }
  280. }
  281. }