1 /* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkThreadPool_DEFINED 9 #define SkThreadPool_DEFINED 10 11 #include "SkCondVar.h" 12 #include "SkRunnable.h" 13 #include "SkTDArray.h" 14 #include "SkTInternalLList.h" 15 #include "SkThreadUtils.h" 16 #include "SkTypes.h" 17 18 #if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID) 19 # include <unistd.h> 20 #endif 21 22 // Returns the number of cores on this machine. 23 static inline int num_cores() { 24 #if defined(SK_BUILD_FOR_WIN32) 25 SYSTEM_INFO sysinfo; 26 GetSystemInfo(&sysinfo); 27 return sysinfo.dwNumberOfProcessors; 28 #elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID) 29 return (int) sysconf(_SC_NPROCESSORS_ONLN); 30 #else 31 return 1; 32 #endif 33 } 34 35 template <typename T> 36 class SkTThreadPool { 37 public: 38 /** 39 * Create a threadpool with count threads, or one thread per core if kThreadPerCore. 40 */ 41 static const int kThreadPerCore = -1; 42 explicit SkTThreadPool(int count); 43 ~SkTThreadPool(); 44 45 /** 46 * Queues up an SkRunnable to run when a thread is available, or synchronously if count is 0. 47 * Does not take ownership. NULL is a safe no-op. If T is not void, the runnable will be passed 48 * a reference to a T on the thread's local stack. 49 */ 50 void add(SkTRunnable<T>*); 51 52 /** 53 * Same as add, but adds the runnable as the very next to run rather than enqueueing it. 54 */ 55 void addNext(SkTRunnable<T>*); 56 57 /** 58 * Block until all added SkRunnables have completed. Once called, calling add() is undefined. 59 */ 60 void wait(); 61 62 private: 63 struct LinkedRunnable { 64 SkTRunnable<T>* fRunnable; // Unowned. 65 SK_DECLARE_INTERNAL_LLIST_INTERFACE(LinkedRunnable); 66 }; 67 68 enum State { 69 kRunning_State, // Normal case. We've been constructed and no one has called wait(). 70 kWaiting_State, // wait has been called, but there still might be work to do or being done. 71 kHalting_State, // There's no work to do and no thread is busy. All threads can shut down. 72 }; 73 74 void addSomewhere(SkTRunnable<T>* r, 75 void (SkTInternalLList<LinkedRunnable>::*)(LinkedRunnable*)); 76 77 SkTInternalLList<LinkedRunnable> fQueue; 78 SkCondVar fReady; 79 SkTDArray<SkThread*> fThreads; 80 State fState; 81 int fBusyThreads; 82 83 static void Loop(void*); // Static because we pass in this. 84 }; 85 86 template <typename T> 87 SkTThreadPool<T>::SkTThreadPool(int count) : fState(kRunning_State), fBusyThreads(0) { 88 if (count < 0) { 89 count = num_cores(); 90 } 91 // Create count threads, all running SkTThreadPool::Loop. 92 for (int i = 0; i < count; i++) { 93 SkThread* thread = SkNEW_ARGS(SkThread, (&SkTThreadPool::Loop, this)); 94 *fThreads.append() = thread; 95 thread->start(); 96 } 97 } 98 99 template <typename T> 100 SkTThreadPool<T>::~SkTThreadPool() { 101 if (kRunning_State == fState) { 102 this->wait(); 103 } 104 } 105 106 namespace SkThreadPoolPrivate { 107 108 template <typename T> 109 struct ThreadLocal { 110 void run(SkTRunnable<T>* r) { r->run(data); } 111 T data; 112 }; 113 114 template <> 115 struct ThreadLocal<void> { 116 void run(SkTRunnable<void>* r) { r->run(); } 117 }; 118 119 } // namespace SkThreadPoolPrivate 120 121 template <typename T> 122 void SkTThreadPool<T>::addSomewhere(SkTRunnable<T>* r, 123 void (SkTInternalLList<LinkedRunnable>::* f)(LinkedRunnable*)) { 124 if (r == NULL) { 125 return; 126 } 127 128 if (fThreads.isEmpty()) { 129 SkThreadPoolPrivate::ThreadLocal<T> threadLocal; 130 threadLocal.run(r); 131 return; 132 } 133 134 LinkedRunnable* linkedRunnable = SkNEW(LinkedRunnable); 135 linkedRunnable->fRunnable = r; 136 fReady.lock(); 137 SkASSERT(fState != kHalting_State); // Shouldn't be able to add work when we're halting. 138 (fQueue.*f)(linkedRunnable); 139 fReady.signal(); 140 fReady.unlock(); 141 } 142 143 template <typename T> 144 void SkTThreadPool<T>::add(SkTRunnable<T>* r) { 145 this->addSomewhere(r, &SkTInternalLList<LinkedRunnable>::addToTail); 146 } 147 148 template <typename T> 149 void SkTThreadPool<T>::addNext(SkTRunnable<T>* r) { 150 this->addSomewhere(r, &SkTInternalLList<LinkedRunnable>::addToHead); 151 } 152 153 154 template <typename T> 155 void SkTThreadPool<T>::wait() { 156 fReady.lock(); 157 fState = kWaiting_State; 158 fReady.broadcast(); 159 fReady.unlock(); 160 161 // Wait for all threads to stop. 162 for (int i = 0; i < fThreads.count(); i++) { 163 fThreads[i]->join(); 164 SkDELETE(fThreads[i]); 165 } 166 SkASSERT(fQueue.isEmpty()); 167 } 168 169 template <typename T> 170 /*static*/ void SkTThreadPool<T>::Loop(void* arg) { 171 // The SkTThreadPool passes itself as arg to each thread as they're created. 172 SkTThreadPool<T>* pool = static_cast<SkTThreadPool<T>*>(arg); 173 SkThreadPoolPrivate::ThreadLocal<T> threadLocal; 174 175 while (true) { 176 // We have to be holding the lock to read the queue and to call wait. 177 pool->fReady.lock(); 178 while(pool->fQueue.isEmpty()) { 179 // Does the client want to stop and are all the threads ready to stop? 180 // If so, we move into the halting state, and whack all the threads so they notice. 181 if (kWaiting_State == pool->fState && pool->fBusyThreads == 0) { 182 pool->fState = kHalting_State; 183 pool->fReady.broadcast(); 184 } 185 // Any time we find ourselves in the halting state, it's quitting time. 186 if (kHalting_State == pool->fState) { 187 pool->fReady.unlock(); 188 return; 189 } 190 // wait yields the lock while waiting, but will have it again when awoken. 191 pool->fReady.wait(); 192 } 193 // We've got the lock back here, no matter if we ran wait or not. 194 195 // The queue is not empty, so we have something to run. Claim it. 196 LinkedRunnable* r = pool->fQueue.head(); 197 198 pool->fQueue.remove(r); 199 200 // Having claimed our SkRunnable, we now give up the lock while we run it. 201 // Otherwise, we'd only ever do work on one thread at a time, which rather 202 // defeats the point of this code. 203 pool->fBusyThreads++; 204 pool->fReady.unlock(); 205 206 // OK, now really do the work. 207 threadLocal.run(r->fRunnable); 208 SkDELETE(r); 209 210 // Let everyone know we're not busy. 211 pool->fReady.lock(); 212 pool->fBusyThreads--; 213 pool->fReady.unlock(); 214 } 215 216 SkASSERT(false); // Unreachable. The only exit happens when pool->fState is kHalting_State. 217 } 218 219 typedef SkTThreadPool<void> SkThreadPool; 220 221 #endif 222