blob: c99c5c4188a5b636f8213ce655f5138fb28f79a0 [file] [log] [blame]
scroggo@google.com4177ef42012-10-31 15:52:16 +00001/*
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"
commit-bot@chromium.orga7538ba2013-10-10 18:49:04 +000012#include "SkRunnable.h"
scroggo@google.com4177ef42012-10-31 15:52:16 +000013#include "SkTDArray.h"
bsalomon@google.com42619d82012-12-03 14:54:59 +000014#include "SkTInternalLList.h"
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000015#include "SkThreadUtils.h"
16#include "SkTypes.h"
scroggo@google.com4177ef42012-10-31 15:52:16 +000017
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000018#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
19# include <unistd.h>
20#endif
scroggo@google.com4177ef42012-10-31 15:52:16 +000021
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000022// Returns the number of cores on this machine.
23static 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)
commit-bot@chromium.orgf8a8ae12014-05-04 00:46:50 +000029 return (int) sysconf(_SC_NPROCESSORS_ONLN);
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000030#else
31 return 1;
32#endif
33}
scroggo@google.com4177ef42012-10-31 15:52:16 +000034
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000035template <typename T>
36class SkTThreadPool {
scroggo@google.com4177ef42012-10-31 15:52:16 +000037public:
38 /**
commit-bot@chromium.org44c661f2013-04-22 15:23:14 +000039 * Create a threadpool with count threads, or one thread per core if kThreadPerCore.
scroggo@google.com4177ef42012-10-31 15:52:16 +000040 */
commit-bot@chromium.org44c661f2013-04-22 15:23:14 +000041 static const int kThreadPerCore = -1;
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000042 explicit SkTThreadPool(int count);
43 ~SkTThreadPool();
scroggo@google.com4177ef42012-10-31 15:52:16 +000044
45 /**
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000046 * 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.
scroggo@google.com4177ef42012-10-31 15:52:16 +000049 */
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000050 void add(SkTRunnable<T>*);
scroggo@google.com4177ef42012-10-31 15:52:16 +000051
commit-bot@chromium.orga7538ba2013-10-10 18:49:04 +000052 /**
commit-bot@chromium.org3f032152014-05-01 17:41:32 +000053 * 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 /**
commit-bot@chromium.orga7538ba2013-10-10 18:49:04 +000058 * Block until all added SkRunnables have completed. Once called, calling add() is undefined.
59 */
60 void wait();
61
scroggo@google.com4177ef42012-10-31 15:52:16 +000062 private:
63 struct LinkedRunnable {
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000064 SkTRunnable<T>* fRunnable; // Unowned.
bsalomon@google.com42619d82012-12-03 14:54:59 +000065 SK_DECLARE_INTERNAL_LLIST_INTERFACE(LinkedRunnable);
scroggo@google.com4177ef42012-10-31 15:52:16 +000066 };
67
commit-bot@chromium.org6ee68582013-10-18 14:19:19 +000068 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
commit-bot@chromium.org3f032152014-05-01 17:41:32 +000074 void addSomewhere(SkTRunnable<T>* r,
75 void (SkTInternalLList<LinkedRunnable>::*)(LinkedRunnable*));
76
commit-bot@chromium.org6ee68582013-10-18 14:19:19 +000077 SkTInternalLList<LinkedRunnable> fQueue;
78 SkCondVar fReady;
79 SkTDArray<SkThread*> fThreads;
80 State fState;
81 int fBusyThreads;
scroggo@google.com4177ef42012-10-31 15:52:16 +000082
83 static void Loop(void*); // Static because we pass in this.
84};
85
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +000086template <typename T>
87SkTThreadPool<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
99template <typename T>
100SkTThreadPool<T>::~SkTThreadPool() {
101 if (kRunning_State == fState) {
102 this->wait();
103 }
104}
105
106namespace SkThreadPoolPrivate {
107
108template <typename T>
109struct ThreadLocal {
110 void run(SkTRunnable<T>* r) { r->run(data); }
111 T data;
112};
113
114template <>
115struct ThreadLocal<void> {
116 void run(SkTRunnable<void>* r) { r->run(); }
117};
118
119} // namespace SkThreadPoolPrivate
120
121template <typename T>
commit-bot@chromium.org3f032152014-05-01 17:41:32 +0000122void SkTThreadPool<T>::addSomewhere(SkTRunnable<T>* r,
123 void (SkTInternalLList<LinkedRunnable>::* f)(LinkedRunnable*)) {
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +0000124 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.
commit-bot@chromium.org3f032152014-05-01 17:41:32 +0000138 (fQueue.*f)(linkedRunnable);
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +0000139 fReady.signal();
140 fReady.unlock();
141}
142
commit-bot@chromium.org3f032152014-05-01 17:41:32 +0000143template <typename T>
144void SkTThreadPool<T>::add(SkTRunnable<T>* r) {
145 this->addSomewhere(r, &SkTInternalLList<LinkedRunnable>::addToTail);
146}
147
148template <typename T>
149void SkTThreadPool<T>::addNext(SkTRunnable<T>* r) {
150 this->addSomewhere(r, &SkTInternalLList<LinkedRunnable>::addToHead);
151}
152
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +0000153
154template <typename T>
155void 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
169template <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.
commit-bot@chromium.org3f032152014-05-01 17:41:32 +0000196 LinkedRunnable* r = pool->fQueue.head();
commit-bot@chromium.orgef57b7e2014-02-28 20:31:31 +0000197
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
219typedef SkTThreadPool<void> SkThreadPool;
220
scroggo@google.com4177ef42012-10-31 15:52:16 +0000221#endif