Files
mongo/util/concurrency/thread_pool.cpp

142 lines
4.0 KiB
C++
Raw Normal View History

2009-12-22 12:13:40 -05:00
/* threadpool.cpp
*/
/* Copyright 2009 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2010-04-27 15:27:52 -04:00
#include "pch.h"
2009-12-22 12:13:40 -05:00
#include "thread_pool.h"
2009-12-22 16:42:49 -05:00
#include "mvar.h"
2009-12-22 12:13:40 -05:00
2011-01-04 00:40:41 -05:00
namespace mongo {
namespace threadpool {
2010-05-26 00:46:49 -04:00
// Worker thread
class Worker : boost::noncopyable {
public:
explicit Worker(ThreadPool& owner)
: _owner(owner)
, _is_done(true)
, _thread(boost::bind(&Worker::loop, this))
{}
// destructor will block until current operation is completed
// Acts as a "join" on this thread
2011-01-04 00:40:41 -05:00
~Worker() {
2010-05-26 00:46:49 -04:00
_task.put(Task());
_thread.join();
}
2011-01-04 00:40:41 -05:00
void set_task(Task& func) {
2010-05-26 00:46:49 -04:00
assert(!func.empty());
assert(_is_done);
_is_done = false;
_task.put(func);
}
2011-01-04 00:40:41 -05:00
private:
2010-05-26 00:46:49 -04:00
ThreadPool& _owner;
MVar<Task> _task;
bool _is_done; // only used for error detection
boost::thread _thread;
2011-01-04 00:40:41 -05:00
void loop() {
2010-05-26 00:46:49 -04:00
while (true) {
Task task = _task.take();
if (task.empty())
break; // ends the thread
try {
task();
2011-01-04 00:40:41 -05:00
}
catch (std::exception e) {
2010-05-26 00:46:49 -04:00
log() << "Unhandled exception in worker thread: " << e.what() << endl;;
2011-01-04 00:40:41 -05:00
}
catch (...) {
2010-05-26 00:46:49 -04:00
log() << "Unhandled non-exception in worker thread" << endl;
}
_is_done = true;
_owner.task_done(this);
}
}
};
ThreadPool::ThreadPool(int nThreads)
: _mutex("ThreadPool"), _tasksRemaining(0)
2011-01-04 00:40:41 -05:00
, _nThreads(nThreads) {
2010-05-26 00:46:49 -04:00
scoped_lock lock(_mutex);
2011-01-04 00:40:41 -05:00
while (nThreads-- > 0) {
2010-05-26 00:46:49 -04:00
Worker* worker = new Worker(*this);
_freeWorkers.push_front(worker);
}
}
2011-01-04 00:40:41 -05:00
ThreadPool::~ThreadPool() {
2010-05-26 00:46:49 -04:00
join();
assert(_tasks.empty());
// O(n) but n should be small
assert(_freeWorkers.size() == (unsigned)_nThreads);
2011-01-04 00:40:41 -05:00
while(!_freeWorkers.empty()) {
2010-05-26 00:46:49 -04:00
delete _freeWorkers.front();
_freeWorkers.pop_front();
}
}
2011-01-04 00:40:41 -05:00
void ThreadPool::join() {
2010-05-26 00:46:49 -04:00
scoped_lock lock(_mutex);
2011-01-04 00:40:41 -05:00
while(_tasksRemaining) {
2010-05-26 00:46:49 -04:00
_condition.wait(lock.boost());
}
}
2011-01-04 00:40:41 -05:00
void ThreadPool::schedule(Task task) {
2010-05-26 00:46:49 -04:00
scoped_lock lock(_mutex);
_tasksRemaining++;
2011-01-04 00:40:41 -05:00
if (!_freeWorkers.empty()) {
2010-05-26 00:46:49 -04:00
_freeWorkers.front()->set_task(task);
_freeWorkers.pop_front();
2011-01-04 00:40:41 -05:00
}
else {
2010-05-26 00:46:49 -04:00
_tasks.push_back(task);
2009-12-22 12:13:40 -05:00
}
}
2010-05-26 00:46:49 -04:00
// should only be called by a worker from the worker thread
2011-01-04 00:40:41 -05:00
void ThreadPool::task_done(Worker* worker) {
2010-05-26 00:46:49 -04:00
scoped_lock lock(_mutex);
2011-01-04 00:40:41 -05:00
if (!_tasks.empty()) {
2010-05-26 00:46:49 -04:00
worker->set_task(_tasks.front());
_tasks.pop_front();
2011-01-04 00:40:41 -05:00
}
else {
2010-05-26 00:46:49 -04:00
_freeWorkers.push_front(worker);
}
_tasksRemaining--;
if(_tasksRemaining == 0)
_condition.notify_all();
}
} //namespace threadpool
2009-12-22 12:13:40 -05:00
} //namespace mongo