PageRenderTime 72ms CodeModel.GetById 58ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 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}