/sparrowhawk/foundation/ESFThreadPool.cpp
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 17#ifndef ESF_THREAD_POOL_H 18#include <ESFThreadPool.h> 19#endif 20 21#ifndef ESF_SYSTEM_ALLOCATOR_H 22#include <ESFSystemAllocator.h> 23#endif 24 25#ifndef ESF_NULL_LOGGER_H 26#include <ESFNullLogger.h> 27#endif 28 29#define MIN_THREADS 1 30 31class ESFThreadPoolWorker: public ESFThread { 32public: 33 ESFThreadPoolWorker(int workerId, const char *name, ESFSharedEmbeddedQueue *queue, ESFLogger *logger); 34 35 virtual ~ESFThreadPoolWorker(); 36 37 virtual void run(); 38 39private: 40 41 //Disabled 42 ESFThreadPoolWorker(const ESFThreadPoolWorker &worker); 43 void operator=(const ESFThreadPoolWorker &worker); 44 45 int _workerId; 46 const char *_name; 47 ESFSharedEmbeddedQueue *_queue; 48 ESFLogger *_logger; 49}; 50 51ESFThreadPool::ESFThreadPool(const char *name, int threads, ESFLogger *logger, ESFAllocator *allocator) : 52 _numThreads(threads < MIN_THREADS ? MIN_THREADS : threads), _name(name ? name : "ThreadPool"), _threads(0), 53 _allocator(allocator ? allocator : ESFSystemAllocator::GetInstance()), _logger(logger ? logger 54 : ESFNullLogger::GetInstance()), _queue() { 55} 56 57ESFThreadPool::~ESFThreadPool() { 58} 59 60ESFError ESFThreadPool::start() { 61 if (_logger->isLoggable(ESFLogger::Notice)) { 62 _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] starting", _name); 63 } 64 65 if (false == createWorkerThreads()) { 66 if (_logger->isLoggable(ESFLogger::Critical)) { 67 _logger->log(ESFLogger::Critical, __FILE__, __LINE__, "[%s] cannot start: Out Of Memory"); 68 } 69 70 return ESF_OUT_OF_MEMORY; 71 } 72 73 ESFError error; 74 75 for (int i = 0; i < _numThreads; ++i) { 76 if (_logger->isLoggable(ESFLogger::Notice)) { 77 _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] starting worker %d", _name, i + 1); 78 } 79 80 error = _threads[i]->start(); 81 82 if (ESF_SUCCESS != error) { 83 if (_logger->isLoggable(ESFLogger::Critical)) { 84 char buffer[100]; 85 86 ESFDescribeError(error, buffer, sizeof(buffer)); 87 88 _logger->log(ESFLogger::Critical, __FILE__, __LINE__, "[%s] cannot start worker %d: %s", _name, i + 1, 89 buffer); 90 } 91 92 _numThreads = i; 93 94 stop(); 95 96 return error; 97 } 98 } 99 100 if (_logger->isLoggable(ESFLogger::Notice)) { 101 _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] started", _name); 102 } 103 104 return ESF_SUCCESS; 105} 106 107void ESFThreadPool::stop() { 108 if (_logger->isLoggable(ESFLogger::Notice)) { 109 _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s] stopping", _name); 110 } 111 112 // worker threads will get ESF_SHUTDOWN next time they try to pull an item from the queue 113 _queue.stop(); 114 115 // stop the worker threads if they are in the middle of running a command 116 for (int i = 0; i < _numThreads; ++i) { 117 _threads[i]->stop(); 118 } 119 120 ESFError error; 121 122 for (int i = 0; i < _numThreads; ++i) { 123 if (_logger->isLoggable(ESFLogger::Debug)) { 124 _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s] waiting for worker %d", _name, i + 1); 125 } 126 127 error = _threads[i]->join(); 128 129 if (ESF_SUCCESS != error) { 130 if (_logger->isLoggable(ESFLogger::Warning)) { 131 char buffer[100]; 132 133 ESFDescribeError(error, buffer, sizeof(buffer)); 134 135 _logger->log(ESFLogger::Warning, __FILE__, __LINE__, "[%s] error joining worker %d: %s", _name, i + 1, 136 buffer); 137 } 138 } 139 } 140 141 destroyWorkerThreads(); 142 143 if (_logger->isLoggable(ESFLogger::Debug)) { 144 _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s] stopped", _name); 145 } 146} 147 148bool ESFThreadPool::createWorkerThreads() { 149 _threads = (ESFThread **) _allocator->allocate(_numThreads * sizeof(ESFThread *)); 150 151 if (!_threads) { 152 return false; 153 } 154 155 for (int i = 0; i < _numThreads; ++i) { 156 _threads[i] = 0; 157 } 158 159 for (int i = 0; i < _numThreads; ++i) { 160 _threads[i] = new (_allocator) ESFThreadPoolWorker(i + 1, _name, &_queue, _logger); 161 162 if (0 == _threads[i]) { 163 destroyWorkerThreads(); 164 165 return false; 166 } 167 } 168 169 return true; 170} 171 172void ESFThreadPool::destroyWorkerThreads() { 173 if (!_threads) { 174 return; 175 } 176 177 for (int i = 0; i < _numThreads; ++i) { 178 if (0 == _threads[i]) { 179 break; 180 } 181 182 _threads[i]->~ESFThread(); 183 _allocator->deallocate(_threads[i]); 184 } 185 186 _allocator->deallocate(_threads); 187 _threads = 0; 188} 189 190ESFThreadPoolWorker::ESFThreadPoolWorker(int workerId, const char *name, ESFSharedEmbeddedQueue *queue, 191 ESFLogger *logger) : 192 _workerId(workerId), _name(name), _queue(queue), _logger(logger) { 193} 194 195ESFThreadPoolWorker::~ESFThreadPoolWorker() { 196} 197 198void ESFThreadPoolWorker::run() { 199 if (_logger->isLoggable(ESFLogger::Notice)) { 200 _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s:%d] starting", _name, _workerId); 201 } 202 203 ESFError error; 204 ESFCommand *command = 0; 205 ESFCleanupHandler *cleanupHandler = 0; 206 int errorCount = 0; 207 bool cleanup = false; 208 209 while (isRunning() && errorCount < 10) { 210 command = (ESFCommand *) _queue->pop(&error); 211 212 if (ESF_SHUTDOWN == error) { 213 if (_logger->isLoggable(ESFLogger::Debug)) { 214 _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s:%d] received shutdown command", _name, 215 _workerId); 216 } 217 218 break; 219 } 220 221 if (ESF_SUCCESS != error) { 222 if (_logger->isLoggable(ESFLogger::Error)) { 223 char buffer[100]; 224 225 ESFDescribeError(error, buffer, sizeof(buffer)); 226 227 _logger->log(ESFLogger::Error, __FILE__, __LINE__, "[%s:%d] cannot pop command from queue: %s", _name, 228 _workerId, buffer); 229 } 230 231 ++errorCount; 232 233 continue; 234 } 235 236 // todo add performance counters 237 238 if (_logger->isLoggable(ESFLogger::Debug)) { 239 _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s:%d] starting command %s", _name, _workerId, 240 command->getName()); 241 } 242 243 cleanup = command->run(&_isRunning); 244 245 if (_logger->isLoggable(ESFLogger::Debug)) { 246 _logger->log(ESFLogger::Debug, __FILE__, __LINE__, "[%s:%d] finished command %s", _name, _workerId, 247 command->getName()); 248 } 249 250 if (cleanup) { 251 cleanupHandler = command->getCleanupHandler(); 252 253 if (cleanupHandler) { 254 cleanupHandler->destroy(command); 255 } 256 } 257 258 command = 0; 259 errorCount = 0; 260 } 261 262 if (_logger->isLoggable(ESFLogger::Notice)) { 263 _logger->log(ESFLogger::Notice, __FILE__, __LINE__, "[%s:%d] exiting", _name, _workerId); 264 } 265}