diff options
-rw-r--r-- | gyp/utils.gyp | 18 | ||||
-rw-r--r-- | include/utils/SkCondVar.h | 45 | ||||
-rw-r--r-- | include/utils/SkCountdown.h | 36 | ||||
-rw-r--r-- | include/utils/SkRunnable.h | 16 | ||||
-rw-r--r-- | include/utils/SkThreadPool.h | 50 | ||||
-rw-r--r-- | src/utils/SkCondVar.cpp | 38 | ||||
-rw-r--r-- | src/utils/SkCountdown.cpp | 33 | ||||
-rw-r--r-- | src/utils/SkThreadPool.cpp | 88 |
8 files changed, 324 insertions, 0 deletions
diff --git a/gyp/utils.gyp b/gyp/utils.gyp index cc25349045..61cfd127b7 100644 --- a/gyp/utils.gyp +++ b/gyp/utils.gyp @@ -18,6 +18,15 @@ '../src/utils', ], 'sources': [ + # Classes for a threadpool. + '../include/utils/SkCondVar.h', + '../include/utils/SkCountdown.h', + '../include/utils/SkRunnable.h', + '../include/utils/SkThreadPool.h', + '../src/utils/SkCondVar.cpp', + '../src/utils/SkCountdown.cpp', + '../src/utils/SkThreadPool.cpp', + '../include/utils/SkBoundaryPatch.h', '../include/utils/SkCamera.h', '../include/utils/SkCubicInterval.h', @@ -147,6 +156,15 @@ '../src/utils/SkThreadUtils_pthread.cpp', '../src/utils/SkThreadUtils_pthread.h', '../src/utils/SkThreadUtils_pthread_other.cpp', + + # SkThreadPool and related currently depend on pthread. + '../include/utils/SkCondVar.h', + '../include/utils/SkCountdown.h', + '../include/utils/SkRunnable.h', + '../include/utils/SkThreadPool.h', + '../src/utils/SkCondVar.cpp', + '../src/utils/SkCountdown.cpp', + '../src/utils/SkThreadPool.cpp', ], },{ #else if 'skia_os != "win"' 'include_dirs!': [ diff --git a/include/utils/SkCondVar.h b/include/utils/SkCondVar.h new file mode 100644 index 0000000000..d2539e4b9e --- /dev/null +++ b/include/utils/SkCondVar.h @@ -0,0 +1,45 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCondVar_DEFINED +#define SkCondVar_DEFINED + +#include <pthread.h> + +class SkCondVar { +public: + SkCondVar(); + ~SkCondVar(); + + void lock(); + void unlock(); + + /** + * Pause the calling thread. Will be awoken when signal() is called. + * Must be called while lock() is held (but gives it up while waiting). + */ + void wait(); + + /** + * Wake one thread waiting on this condition. Must be called while lock() + * is held. + */ + void signal(); + + /** + * Wake all threads waiting on this condition. Must be called while lock() + * is held. + */ + void broadcast(); + +private: + // FIXME: Make this independent of pthreads. + pthread_mutex_t fMutex; + pthread_cond_t fCond; +}; + +#endif diff --git a/include/utils/SkCountdown.h b/include/utils/SkCountdown.h new file mode 100644 index 0000000000..6bcec7d5ff --- /dev/null +++ b/include/utils/SkCountdown.h @@ -0,0 +1,36 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkCountdown_DEFINED +#define SkCountdown_DEFINED + +#include "SkCondVar.h" +#include "SkRunnable.h" +#include "SkTypes.h" + +class SkCountdown : public SkRunnable { +public: + explicit SkCountdown(int32_t count); + + /** + * Resets the countdown to the count provided. + */ + void reset(int32_t count); + + virtual void run() SK_OVERRIDE; + + /** + * Blocks until run() has been called count times. + */ + void wait(); + +private: + SkCondVar fReady; + int32_t fCount; +}; + +#endif diff --git a/include/utils/SkRunnable.h b/include/utils/SkRunnable.h new file mode 100644 index 0000000000..c7bedcc979 --- /dev/null +++ b/include/utils/SkRunnable.h @@ -0,0 +1,16 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRunnable_DEFINED +#define SkRunnable_DEFINED + +class SkRunnable { +public: + virtual void run() = 0; +}; + +#endif diff --git a/include/utils/SkThreadPool.h b/include/utils/SkThreadPool.h new file mode 100644 index 0000000000..a96f87b275 --- /dev/null +++ b/include/utils/SkThreadPool.h @@ -0,0 +1,50 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkThreadPool_DEFINED +#define SkThreadPool_DEFINED + +#include "SkCondVar.h" +#include "SkTDArray.h" +#include "SkTDLinkedList.h" + +class SkRunnable; +class SkThread; + +class SkThreadPool { + +public: + /** + * Create a threadpool with exactly count (>=0) threads. + */ + explicit SkThreadPool(int count); + ~SkThreadPool(); + + /** + * Queues up an SkRunnable to run when a thread is available, or immediately if + * count is 0. NULL is a safe no-op. Does not take ownership. + */ + void add(SkRunnable*); + + private: + struct LinkedRunnable { + // Unowned pointer. + SkRunnable* fRunnable; + + private: + SK_DEFINE_DLINKEDLIST_INTERFACE(LinkedRunnable) + }; + + SkTDLinkedList<LinkedRunnable> fQueue; + SkCondVar fReady; + SkTDArray<SkThread*> fThreads; + bool fDone; + + static void Loop(void*); // Static because we pass in this. +}; + +#endif 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(); +} |