class DelegateSimpleThreadPool : public DelegateSimpleThread::Delegate {
public:
typedef DelegateSimpleThread::Delegate Delegate;
DelegateSimpleThreadPool(const std::string name_prefix, int num_threads)
: name_prefix_(name_prefix), num_threads_(num_threads),
dry_(true, false) { }
~DelegateSimpleThreadPool();
// Start up all of the underlying threads, and start processing work if we
// have any.
void Start();
// Make sure all outstanding work is finished, and wait for and destroy all
// of the underlying threads in the pool.
void JoinAll();
// It is safe to AddWork() any time, before or after Start().
// Delegate* should always be a valid pointer, NULL is reserved internally.
void AddWork(Delegate* work, int repeat_count);
void AddWork(Delegate* work) {
AddWork(work, 1);
}
// We implement the Delegate interface, for running our internal threads.
virtual void Run();
private:
const std::string name_prefix_;
int num_threads_;
std::vector<DelegateSimpleThread*> threads_;
std::queue<Delegate*> delegates_;
Lock lock_; // Locks delegates_
WaitableEvent dry_; // Not signaled when there is no work to do.
};
DelegateSimpleThreadPool::~DelegateSimpleThreadPool() {
DCHECK(threads_.empty());
DCHECK(delegates_.empty());
DCHECK(!dry_.IsSignaled());
}
void DelegateSimpleThreadPool::Start() {
DCHECK(threads_.empty()) << "Start() called with outstanding threads.";
for (int i = 0; i < num_threads_; ++i) {
DelegateSimpleThread* thread = new DelegateSimpleThread(this, name_prefix_);
thread->Start();
threads_.push_back(thread);
}
}
void DelegateSimpleThreadPool::JoinAll() {
DCHECK(!threads_.empty()) << "JoinAll() called with no outstanding threads.";
// Tell all our threads to quit their worker loop.
AddWork(NULL, num_threads_);
// Join and destroy all the worker threads.
for (int i = 0; i < num_threads_; ++i) {
threads_[i]->Join();
delete threads_[i];
}
threads_.clear();
DCHECK(delegates_.empty());
}
void DelegateSimpleThreadPool::AddWork(Delegate* delegate, int repeat_count) {
AutoLock locked(lock_);
for (int i = 0; i < repeat_count; ++i)
delegates_.push(delegate);
// If we were empty, signal that we have work now.
if (!dry_.IsSignaled())
dry_.Signal();
}
void DelegateSimpleThreadPool::Run() {
Delegate* work;
while (true) {
dry_.Wait();
{
AutoLock locked(lock_);
if (!dry_.IsSignaled())
continue;
DCHECK(!delegates_.empty());
work = delegates_.front();
delegates_.pop();
// Signal to any other threads that we're currently out of work.
if (delegates_.empty())
dry_.Reset();
}
// A NULL delegate pointer signals us to quit.
if (!work)
break;
work->Run();
}
}