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.
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)
29    return (int) sysconf(_SC_NPROCESSORS_ONLN);
30#else
31    return 1;
32#endif
33}
34
35template <typename T>
36class SkTThreadPool {
37public:
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
86template <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>
122void 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
143template <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
153
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.
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
219typedef SkTThreadPool<void> SkThreadPool;
220
221#endif
222