1406654be7a930b484159f5bca107d3b11d8a9edemtklein#include "SkTaskGroup.h" 2406654be7a930b484159f5bca107d3b11d8a9edemtklein 3406654be7a930b484159f5bca107d3b11d8a9edemtklein#include "SkCondVar.h" 4406654be7a930b484159f5bca107d3b11d8a9edemtklein#include "SkTDArray.h" 5406654be7a930b484159f5bca107d3b11d8a9edemtklein#include "SkThread.h" 6406654be7a930b484159f5bca107d3b11d8a9edemtklein#include "SkThreadUtils.h" 7406654be7a930b484159f5bca107d3b11d8a9edemtklein 8406654be7a930b484159f5bca107d3b11d8a9edemtklein#if defined(SK_BUILD_FOR_WIN32) 9406654be7a930b484159f5bca107d3b11d8a9edemtklein static inline int num_cores() { 10406654be7a930b484159f5bca107d3b11d8a9edemtklein SYSTEM_INFO sysinfo; 11406654be7a930b484159f5bca107d3b11d8a9edemtklein GetSystemInfo(&sysinfo); 12406654be7a930b484159f5bca107d3b11d8a9edemtklein return sysinfo.dwNumberOfProcessors; 13406654be7a930b484159f5bca107d3b11d8a9edemtklein } 14406654be7a930b484159f5bca107d3b11d8a9edemtklein#else 15406654be7a930b484159f5bca107d3b11d8a9edemtklein #include <unistd.h> 16406654be7a930b484159f5bca107d3b11d8a9edemtklein static inline int num_cores() { 17406654be7a930b484159f5bca107d3b11d8a9edemtklein return (int) sysconf(_SC_NPROCESSORS_ONLN); 18406654be7a930b484159f5bca107d3b11d8a9edemtklein } 19406654be7a930b484159f5bca107d3b11d8a9edemtklein#endif 20406654be7a930b484159f5bca107d3b11d8a9edemtklein 21406654be7a930b484159f5bca107d3b11d8a9edemtkleinnamespace { 22406654be7a930b484159f5bca107d3b11d8a9edemtklein 23406654be7a930b484159f5bca107d3b11d8a9edemtkleinclass ThreadPool : SkNoncopyable { 24406654be7a930b484159f5bca107d3b11d8a9edemtkleinpublic: 25406654be7a930b484159f5bca107d3b11d8a9edemtklein static void Add(SkRunnable* task, int32_t* pending) { 26eefd18cef26dcfb67e68d05e1845b35f015a5bcamtklein if (!gGlobal) { // If we have no threads, run synchronously. 27eefd18cef26dcfb67e68d05e1845b35f015a5bcamtklein return task->run(); 28eefd18cef26dcfb67e68d05e1845b35f015a5bcamtklein } 29406654be7a930b484159f5bca107d3b11d8a9edemtklein gGlobal->add(task, pending); 30406654be7a930b484159f5bca107d3b11d8a9edemtklein } 31406654be7a930b484159f5bca107d3b11d8a9edemtklein 32406654be7a930b484159f5bca107d3b11d8a9edemtklein static void Wait(int32_t* pending) { 33eefd18cef26dcfb67e68d05e1845b35f015a5bcamtklein if (!gGlobal) { // If we have no threads, the work must already be done. 34eefd18cef26dcfb67e68d05e1845b35f015a5bcamtklein SkASSERT(*pending == 0); 35eefd18cef26dcfb67e68d05e1845b35f015a5bcamtklein return; 36eefd18cef26dcfb67e68d05e1845b35f015a5bcamtklein } 37406654be7a930b484159f5bca107d3b11d8a9edemtklein while (sk_acquire_load(pending) > 0) { // Pairs with sk_atomic_dec here or in Loop. 38406654be7a930b484159f5bca107d3b11d8a9edemtklein // Lend a hand until our SkTaskGroup of interest is done. 39406654be7a930b484159f5bca107d3b11d8a9edemtklein Work work; 40406654be7a930b484159f5bca107d3b11d8a9edemtklein { 41406654be7a930b484159f5bca107d3b11d8a9edemtklein AutoLock lock(&gGlobal->fReady); 42406654be7a930b484159f5bca107d3b11d8a9edemtklein if (gGlobal->fWork.isEmpty()) { 43406654be7a930b484159f5bca107d3b11d8a9edemtklein // Someone has picked up all the work (including ours). How nice of them! 44406654be7a930b484159f5bca107d3b11d8a9edemtklein // (They may still be working on it, so we can't assert *pending == 0 here.) 45406654be7a930b484159f5bca107d3b11d8a9edemtklein continue; 46406654be7a930b484159f5bca107d3b11d8a9edemtklein } 47406654be7a930b484159f5bca107d3b11d8a9edemtklein gGlobal->fWork.pop(&work); 48406654be7a930b484159f5bca107d3b11d8a9edemtklein } 49406654be7a930b484159f5bca107d3b11d8a9edemtklein // This Work isn't necessarily part of our SkTaskGroup of interest, but that's fine. 50406654be7a930b484159f5bca107d3b11d8a9edemtklein // We threads gotta stick together. We're always making forward progress. 51406654be7a930b484159f5bca107d3b11d8a9edemtklein work.task->run(); 52406654be7a930b484159f5bca107d3b11d8a9edemtklein sk_atomic_dec(work.pending); // Release pairs with the sk_acquire_load() just above. 53406654be7a930b484159f5bca107d3b11d8a9edemtklein } 54406654be7a930b484159f5bca107d3b11d8a9edemtklein } 55406654be7a930b484159f5bca107d3b11d8a9edemtklein 56406654be7a930b484159f5bca107d3b11d8a9edemtkleinprivate: 57406654be7a930b484159f5bca107d3b11d8a9edemtklein struct AutoLock { 58406654be7a930b484159f5bca107d3b11d8a9edemtklein AutoLock(SkCondVar* c) : fC(c) { fC->lock(); } 59406654be7a930b484159f5bca107d3b11d8a9edemtklein ~AutoLock() { fC->unlock(); } 60406654be7a930b484159f5bca107d3b11d8a9edemtklein private: 61406654be7a930b484159f5bca107d3b11d8a9edemtklein SkCondVar* fC; 62406654be7a930b484159f5bca107d3b11d8a9edemtklein }; 63406654be7a930b484159f5bca107d3b11d8a9edemtklein 64406654be7a930b484159f5bca107d3b11d8a9edemtklein struct Work { 65406654be7a930b484159f5bca107d3b11d8a9edemtklein SkRunnable* task; // A task to ->run(), 66406654be7a930b484159f5bca107d3b11d8a9edemtklein int32_t* pending; // then sk_atomic_dec(pending) afterwards. 67406654be7a930b484159f5bca107d3b11d8a9edemtklein }; 68406654be7a930b484159f5bca107d3b11d8a9edemtklein 69406654be7a930b484159f5bca107d3b11d8a9edemtklein explicit ThreadPool(int threads) : fDraining(false) { 70406654be7a930b484159f5bca107d3b11d8a9edemtklein if (threads == 0) { 71406654be7a930b484159f5bca107d3b11d8a9edemtklein threads = num_cores(); 72406654be7a930b484159f5bca107d3b11d8a9edemtklein } 73406654be7a930b484159f5bca107d3b11d8a9edemtklein for (int i = 0; i < threads; i++) { 74406654be7a930b484159f5bca107d3b11d8a9edemtklein fThreads.push(SkNEW_ARGS(SkThread, (&ThreadPool::Loop, this))); 75406654be7a930b484159f5bca107d3b11d8a9edemtklein fThreads.top()->start(); 76406654be7a930b484159f5bca107d3b11d8a9edemtklein } 77406654be7a930b484159f5bca107d3b11d8a9edemtklein } 78406654be7a930b484159f5bca107d3b11d8a9edemtklein 79406654be7a930b484159f5bca107d3b11d8a9edemtklein ~ThreadPool() { 80406654be7a930b484159f5bca107d3b11d8a9edemtklein SkASSERT(fWork.isEmpty()); // All SkTaskGroups should be destroyed by now. 81406654be7a930b484159f5bca107d3b11d8a9edemtklein { 82406654be7a930b484159f5bca107d3b11d8a9edemtklein AutoLock lock(&fReady); 83406654be7a930b484159f5bca107d3b11d8a9edemtklein fDraining = true; 84406654be7a930b484159f5bca107d3b11d8a9edemtklein fReady.broadcast(); 85406654be7a930b484159f5bca107d3b11d8a9edemtklein } 86406654be7a930b484159f5bca107d3b11d8a9edemtklein for (int i = 0; i < fThreads.count(); i++) { 87406654be7a930b484159f5bca107d3b11d8a9edemtklein fThreads[i]->join(); 88406654be7a930b484159f5bca107d3b11d8a9edemtklein } 89406654be7a930b484159f5bca107d3b11d8a9edemtklein SkASSERT(fWork.isEmpty()); // Can't hurt to double check. 90406654be7a930b484159f5bca107d3b11d8a9edemtklein fThreads.deleteAll(); 91406654be7a930b484159f5bca107d3b11d8a9edemtklein } 92406654be7a930b484159f5bca107d3b11d8a9edemtklein 93406654be7a930b484159f5bca107d3b11d8a9edemtklein void add(SkRunnable* task, int32_t* pending) { 94406654be7a930b484159f5bca107d3b11d8a9edemtklein Work work = { task, pending }; 95406654be7a930b484159f5bca107d3b11d8a9edemtklein sk_atomic_inc(pending); // No barrier needed. 96406654be7a930b484159f5bca107d3b11d8a9edemtklein { 97406654be7a930b484159f5bca107d3b11d8a9edemtklein AutoLock lock(&fReady); 98406654be7a930b484159f5bca107d3b11d8a9edemtklein fWork.push(work); 99406654be7a930b484159f5bca107d3b11d8a9edemtklein fReady.signal(); 100406654be7a930b484159f5bca107d3b11d8a9edemtklein } 101406654be7a930b484159f5bca107d3b11d8a9edemtklein } 102406654be7a930b484159f5bca107d3b11d8a9edemtklein 103406654be7a930b484159f5bca107d3b11d8a9edemtklein static void Loop(void* arg) { 104406654be7a930b484159f5bca107d3b11d8a9edemtklein ThreadPool* pool = (ThreadPool*)arg; 105406654be7a930b484159f5bca107d3b11d8a9edemtklein Work work; 106406654be7a930b484159f5bca107d3b11d8a9edemtklein while (true) { 107406654be7a930b484159f5bca107d3b11d8a9edemtklein { 108406654be7a930b484159f5bca107d3b11d8a9edemtklein AutoLock lock(&pool->fReady); 109406654be7a930b484159f5bca107d3b11d8a9edemtklein while (pool->fWork.isEmpty()) { 110406654be7a930b484159f5bca107d3b11d8a9edemtklein if (pool->fDraining) { 111406654be7a930b484159f5bca107d3b11d8a9edemtklein return; 112406654be7a930b484159f5bca107d3b11d8a9edemtklein } 113406654be7a930b484159f5bca107d3b11d8a9edemtklein pool->fReady.wait(); 114406654be7a930b484159f5bca107d3b11d8a9edemtklein } 115406654be7a930b484159f5bca107d3b11d8a9edemtklein pool->fWork.pop(&work); 116406654be7a930b484159f5bca107d3b11d8a9edemtklein } 117406654be7a930b484159f5bca107d3b11d8a9edemtklein work.task->run(); 118406654be7a930b484159f5bca107d3b11d8a9edemtklein sk_atomic_dec(work.pending); // Release pairs with sk_acquire_load() in Wait(). 119406654be7a930b484159f5bca107d3b11d8a9edemtklein } 120406654be7a930b484159f5bca107d3b11d8a9edemtklein } 121406654be7a930b484159f5bca107d3b11d8a9edemtklein 122406654be7a930b484159f5bca107d3b11d8a9edemtklein SkTDArray<Work> fWork; 123406654be7a930b484159f5bca107d3b11d8a9edemtklein SkTDArray<SkThread*> fThreads; 124406654be7a930b484159f5bca107d3b11d8a9edemtklein SkCondVar fReady; 125406654be7a930b484159f5bca107d3b11d8a9edemtklein bool fDraining; 126406654be7a930b484159f5bca107d3b11d8a9edemtklein 127406654be7a930b484159f5bca107d3b11d8a9edemtklein static ThreadPool* gGlobal; 128406654be7a930b484159f5bca107d3b11d8a9edemtklein friend struct SkTaskGroup::Enabler; 129406654be7a930b484159f5bca107d3b11d8a9edemtklein}; 130406654be7a930b484159f5bca107d3b11d8a9edemtkleinThreadPool* ThreadPool::gGlobal = NULL; 131406654be7a930b484159f5bca107d3b11d8a9edemtklein 132406654be7a930b484159f5bca107d3b11d8a9edemtklein} // namespace 133406654be7a930b484159f5bca107d3b11d8a9edemtklein 134406654be7a930b484159f5bca107d3b11d8a9edemtkleinSkTaskGroup::Enabler::Enabler(int threads) { 135406654be7a930b484159f5bca107d3b11d8a9edemtklein SkASSERT(ThreadPool::gGlobal == NULL); 136406654be7a930b484159f5bca107d3b11d8a9edemtklein ThreadPool::gGlobal = SkNEW_ARGS(ThreadPool, (threads)); 137406654be7a930b484159f5bca107d3b11d8a9edemtklein} 138406654be7a930b484159f5bca107d3b11d8a9edemtklein 139406654be7a930b484159f5bca107d3b11d8a9edemtkleinSkTaskGroup::Enabler::~Enabler() { 140406654be7a930b484159f5bca107d3b11d8a9edemtklein SkASSERT(ThreadPool::gGlobal != NULL); 141406654be7a930b484159f5bca107d3b11d8a9edemtklein SkDELETE(ThreadPool::gGlobal); 142406654be7a930b484159f5bca107d3b11d8a9edemtklein} 143406654be7a930b484159f5bca107d3b11d8a9edemtklein 144406654be7a930b484159f5bca107d3b11d8a9edemtkleinSkTaskGroup::SkTaskGroup() : fPending(0) {} 145406654be7a930b484159f5bca107d3b11d8a9edemtklein 146406654be7a930b484159f5bca107d3b11d8a9edemtkleinvoid SkTaskGroup::add(SkRunnable* task) { ThreadPool::Add(task, &fPending); } 147406654be7a930b484159f5bca107d3b11d8a9edemtkleinvoid SkTaskGroup::wait() { ThreadPool::Wait(&fPending); } 148406654be7a930b484159f5bca107d3b11d8a9edemtklein 149