blob: 5fe10255b5ae599945cd4fa3c5051a35dcdb05f3 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkRunnable.h"
#include "SkThreadPool.h"
#include "SkThreadUtils.h"
#include "SkTypes.h"
#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
#include <unistd.h>
#endif
// Returns the number of cores on this machine.
static int num_cores() {
#if defined(SK_BUILD_FOR_WIN32)
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
return sysconf(_SC_NPROCESSORS_ONLN);
#else
return 1;
#endif
}
SkThreadPool::SkThreadPool(int count)
: fDone(false) {
if (count < 0) count = num_cores();
// Create count threads, all running SkThreadPool::Loop.
for (int i = 0; i < count; i++) {
SkThread* thread = SkNEW_ARGS(SkThread, (&SkThreadPool::Loop, this));
*fThreads.append() = thread;
thread->start();
}
}
SkThreadPool::~SkThreadPool() {
fDone = true;
fReady.lock();
fReady.broadcast();
fReady.unlock();
// Wait for all threads to stop.
for (int i = 0; i < fThreads.count(); i++) {
fThreads[i]->join();
SkDELETE(fThreads[i]);
}
}
/*static*/ void SkThreadPool::Loop(void* arg) {
// The SkThreadPool passes itself as arg to each thread as they're created.
SkThreadPool* pool = static_cast<SkThreadPool*>(arg);
while (true) {
// We have to be holding the lock to read the queue and to call wait.
pool->fReady.lock();
while(pool->fQueue.isEmpty()) {
// Is it time to die?
if (pool->fDone) {
pool->fReady.unlock();
return;
}
// wait yields the lock while waiting, but will have it again when awoken.
pool->fReady.wait();
}
// We've got the lock back here, no matter if we ran wait or not.
// The queue is not empty, so we have something to run. Claim it.
LinkedRunnable* r = pool->fQueue.tail();
pool->fQueue.remove(r);
// Having claimed our SkRunnable, we now give up the lock while we run it.
// Otherwise, we'd only ever do work on one thread at a time, which rather
// defeats the point of this code.
pool->fReady.unlock();
// OK, now really do the work.
r->fRunnable->run();
SkDELETE(r);
}
SkASSERT(false); // Unreachable. The only exit happens when pool->fDone.
}
void SkThreadPool::add(SkRunnable* r) {
if (NULL == r) {
return;
}
// If we don't have any threads, obligingly just run the thing now.
if (fThreads.isEmpty()) {
return r->run();
}
// We have some threads. Queue it up!
fReady.lock();
LinkedRunnable* linkedRunnable = SkNEW(LinkedRunnable);
linkedRunnable->fRunnable = r;
fQueue.addToHead(linkedRunnable);
fReady.signal();
fReady.unlock();
}