/sparrowhawk/foundation/ESFThreadPool.cpp

http://github.com/jtblatt/duderino · C++ · 265 lines · 178 code · 68 blank · 19 comment · 41 complexity · 9f0f4b6ba48f92c0aec57293ba315d7d MD5 · raw file

  1. /** @file ESFThreadPool.cpp
  2. * @brief A thread pool that executes ESFCommands
  3. *
  4. * Copyright (c) 2009 Yahoo! Inc.
  5. * The copyrights embodied in the content of this file are licensed by Yahoo! Inc.
  6. * under the BSD (revised) open source license.
  7. *
  8. * Derived from code that is Copyright (c) 2009 Joshua Blatt and offered under both
  9. * BSD and Apache 2.0 licenses (http://sourceforge.net/projects/sparrowhawk/).
  10. *
  11. * $Author: blattj $
  12. * $Date: 2009/05/25 21:51:08 $
  13. * $Name: $
  14. * $Revision: 1.3 $
  15. */
  16. #ifndef ESF_THREAD_POOL_H
  17. #include <ESFThreadPool.h>
  18. #endif
  19. #ifndef ESF_SYSTEM_ALLOCATOR_H
  20. #include <ESFSystemAllocator.h>
  21. #endif
  22. #ifndef ESF_NULL_LOGGER_H
  23. #include <ESFNullLogger.h>
  24. #endif
  25. #define MIN_THREADS 1
  26. class ESFThreadPoolWorker: public ESFThread {
  27. public:
  28. ESFThreadPoolWorker(int workerId, const char *name, ESFSharedEmbeddedQueue *queue, ESFLogger *logger);
  29. virtual ~ESFThreadPoolWorker();
  30. virtual void run();
  31. private:
  32. //Disabled
  33. ESFThreadPoolWorker(const ESFThreadPoolWorker &worker);
  34. void operator=(const ESFThreadPoolWorker &worker);
  35. int _workerId;
  36. const char *_name;
  37. ESFSharedEmbeddedQueue *_queue;
  38. ESFLogger *_logger;
  39. };
  40. ESFThreadPool::ESFThreadPool(const char *name, int threads, ESFLogger *logger, ESFAllocator *allocator) :
  41. _numThreads(threads < MIN_THREADS ? MIN_THREADS : threads), _name(name ? name : "ThreadPool"), _threads(0),
  42. _allocator(allocator ? allocator : ESFSystemAllocator::GetInstance()), _logger(logger ? logger
  43. : ESFNullLogger::GetInstance()), _queue() {
  44. }
  45. ESFThreadPool::~ESFThreadPool() {
  46. }
  47. ESFError ESFThreadPool::start() {
  48. if (_logger->isLoggable(ESFLogger::Notice)) {
  49. _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] starting", _name);
  50. }
  51. if (false == createWorkerThreads()) {
  52. if (_logger->isLoggable(ESFLogger::Critical)) {
  53. _logger->log(ESFLogger::Critical, __FILE__, __LINE__, "[%s] cannot start: Out Of Memory");
  54. }
  55. return ESF_OUT_OF_MEMORY;
  56. }
  57. ESFError error;
  58. for (int i = 0; i < _numThreads; ++i) {
  59. if (_logger->isLoggable(ESFLogger::Notice)) {
  60. _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] starting worker %d", _name, i + 1);
  61. }
  62. error = _threads[i]->start();
  63. if (ESF_SUCCESS != error) {
  64. if (_logger->isLoggable(ESFLogger::Critical)) {
  65. char buffer[100];
  66. ESFDescribeError(error, buffer, sizeof(buffer));
  67. _logger->log(ESFLogger::Critical, __FILE__, __LINE__, "[%s] cannot start worker %d: %s", _name, i + 1,
  68. buffer);
  69. }
  70. _numThreads = i;
  71. stop();
  72. return error;
  73. }
  74. }
  75. if (_logger->isLoggable(ESFLogger::Notice)) {
  76. _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] started", _name);
  77. }
  78. return ESF_SUCCESS;
  79. }
  80. void ESFThreadPool::stop() {
  81. if (_logger->isLoggable(ESFLogger::Notice)) {
  82. _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] stopping", _name);
  83. }
  84. // worker threads will get ESF_SHUTDOWN next time they try to pull an item from the queue
  85. _queue.stop();
  86. // stop the worker threads if they are in the middle of running a command
  87. for (int i = 0; i < _numThreads; ++i) {
  88. _threads[i]->stop();
  89. }
  90. ESFError error;
  91. for (int i = 0; i < _numThreads; ++i) {
  92. if (_logger->isLoggable(ESFLogger::Debug)) {
  93. _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s] waiting for worker %d", _name, i + 1);
  94. }
  95. error = _threads[i]->join();
  96. if (ESF_SUCCESS != error) {
  97. if (_logger->isLoggable(ESFLogger::Warning)) {
  98. char buffer[100];
  99. ESFDescribeError(error, buffer, sizeof(buffer));
  100. _logger->log(ESFLogger::Warning, __FILE__, __LINE__, "[%s] error joining worker %d: %s", _name, i + 1,
  101. buffer);
  102. }
  103. }
  104. }
  105. destroyWorkerThreads();
  106. if (_logger->isLoggable(ESFLogger::Debug)) {
  107. _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s] stopped", _name);
  108. }
  109. }
  110. bool ESFThreadPool::createWorkerThreads() {
  111. _threads = (ESFThread **) _allocator->allocate(_numThreads * sizeof(ESFThread *));
  112. if (!_threads) {
  113. return false;
  114. }
  115. for (int i = 0; i < _numThreads; ++i) {
  116. _threads[i] = 0;
  117. }
  118. for (int i = 0; i < _numThreads; ++i) {
  119. _threads[i] = new (_allocator) ESFThreadPoolWorker(i + 1, _name, &_queue, _logger);
  120. if (0 == _threads[i]) {
  121. destroyWorkerThreads();
  122. return false;
  123. }
  124. }
  125. return true;
  126. }
  127. void ESFThreadPool::destroyWorkerThreads() {
  128. if (!_threads) {
  129. return;
  130. }
  131. for (int i = 0; i < _numThreads; ++i) {
  132. if (0 == _threads[i]) {
  133. break;
  134. }
  135. _threads[i]->~ESFThread();
  136. _allocator->deallocate(_threads[i]);
  137. }
  138. _allocator->deallocate(_threads);
  139. _threads = 0;
  140. }
  141. ESFThreadPoolWorker::ESFThreadPoolWorker(int workerId, const char *name, ESFSharedEmbeddedQueue *queue,
  142. ESFLogger *logger) :
  143. _workerId(workerId), _name(name), _queue(queue), _logger(logger) {
  144. }
  145. ESFThreadPoolWorker::~ESFThreadPoolWorker() {
  146. }
  147. void ESFThreadPoolWorker::run() {
  148. if (_logger->isLoggable(ESFLogger::Notice)) {
  149. _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s:%d] starting", _name, _workerId);
  150. }
  151. ESFError error;
  152. ESFCommand *command = 0;
  153. ESFCleanupHandler *cleanupHandler = 0;
  154. int errorCount = 0;
  155. bool cleanup = false;
  156. while (isRunning() && errorCount < 10) {
  157. command = (ESFCommand *) _queue->pop(&error);
  158. if (ESF_SHUTDOWN == error) {
  159. if (_logger->isLoggable(ESFLogger::Debug)) {
  160. _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s:%d] received shutdown command", _name,
  161. _workerId);
  162. }
  163. break;
  164. }
  165. if (ESF_SUCCESS != error) {
  166. if (_logger->isLoggable(ESFLogger::Error)) {
  167. char buffer[100];
  168. ESFDescribeError(error, buffer, sizeof(buffer));
  169. _logger->log(ESFLogger::Error, __FILE__, __LINE__, "[%s:%d] cannot pop command from queue: %s", _name,
  170. _workerId, buffer);
  171. }
  172. ++errorCount;
  173. continue;
  174. }
  175. // todo add performance counters
  176. if (_logger->isLoggable(ESFLogger::Debug)) {
  177. _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s:%d] starting command %s", _name, _workerId,
  178. command->getName());
  179. }
  180. cleanup = command->run(&_isRunning);
  181. if (_logger->isLoggable(ESFLogger::Debug)) {
  182. _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s:%d] finished command %s", _name, _workerId,
  183. command->getName());
  184. }
  185. if (cleanup) {
  186. cleanupHandler = command->getCleanupHandler();
  187. if (cleanupHandler) {
  188. cleanupHandler->destroy(command);
  189. }
  190. }
  191. command = 0;
  192. errorCount = 0;
  193. }
  194. if (_logger->isLoggable(ESFLogger::Notice)) {
  195. _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s:%d] exiting", _name, _workerId);
  196. }
  197. }