diff options
author | 2012-10-31 15:52:16 +0000 | |
---|---|---|
committer | 2012-10-31 15:52:16 +0000 | |
commit | 4177ef4b229b5fb67f355569654981bb4bf8eb9c (patch) | |
tree | 0091c5ff6ed7dd7b89e73f9b11cde51e700c5726 /src | |
parent | 72ba668db833d25ecdca4edfbefd601e508a1e62 (diff) |
Add SkThreadPool for managing threads.
Skia-ized from https://codereview.appspot.com/6755043/
TODO: Use SkThread and platform independent features.
Review URL: https://codereview.appspot.com/6777064
git-svn-id: http://skia.googlecode.com/svn/trunk@6217 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r-- | src/utils/SkCondVar.cpp | 38 | ||||
-rw-r--r-- | src/utils/SkCountdown.cpp | 33 | ||||
-rw-r--r-- | src/utils/SkThreadPool.cpp | 88 |
3 files changed, 159 insertions, 0 deletions
diff --git a/src/utils/SkCondVar.cpp b/src/utils/SkCondVar.cpp new file mode 100644 index 0000000000..8cbab58de2 --- /dev/null +++ b/src/utils/SkCondVar.cpp @@ -0,0 +1,38 @@ +/* + * 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 "SkCondVar.h" + +SkCondVar::SkCondVar() { + pthread_mutex_init(&fMutex, NULL /* default mutex attr */); + pthread_cond_init(&fCond, NULL /* default cond attr */); +} + +SkCondVar::~SkCondVar() { + pthread_mutex_destroy(&fMutex); + pthread_cond_destroy(&fCond); +} + +void SkCondVar::lock() { + pthread_mutex_lock(&fMutex); +} + +void SkCondVar::unlock() { + pthread_mutex_unlock(&fMutex); +} + +void SkCondVar::wait() { + pthread_cond_wait(&fCond, &fMutex); +} + +void SkCondVar::signal() { + pthread_cond_signal(&fCond); +} + +void SkCondVar::broadcast() { + pthread_cond_broadcast(&fCond); +} diff --git a/src/utils/SkCountdown.cpp b/src/utils/SkCountdown.cpp new file mode 100644 index 0000000000..5b476cc094 --- /dev/null +++ b/src/utils/SkCountdown.cpp @@ -0,0 +1,33 @@ +/* + * 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 "SkCountdown.h" +#include "SkThread.h" + +SkCountdown::SkCountdown(int32_t count) +: fCount(count) {} + +void SkCountdown::reset(int32_t count) { + fCount = count; +} + +void SkCountdown::run() { + if (sk_atomic_dec(&fCount) == 1) { + fReady.lock(); + fReady.signal(); + fReady.unlock(); + } +} + +void SkCountdown::wait() { + fReady.lock(); + while (fCount > 0) { + fReady.wait(); + } + fReady.unlock(); +} + diff --git a/src/utils/SkThreadPool.cpp b/src/utils/SkThreadPool.cpp new file mode 100644 index 0000000000..78cb417d07 --- /dev/null +++ b/src/utils/SkThreadPool.cpp @@ -0,0 +1,88 @@ +/* + * 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 "SkThreadPool.h" +#include "SkRunnable.h" +#include "SkThreadUtils.h" + +SkThreadPool::SkThreadPool(const int count) +: fDone(false) { + // 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(); +} |