gfx/skia/trunk/include/utils/SkThreadPool.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/include/utils/SkThreadPool.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,202 @@
     1.4 +/*
     1.5 + * Copyright 2012 Google Inc.
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#ifndef SkThreadPool_DEFINED
    1.12 +#define SkThreadPool_DEFINED
    1.13 +
    1.14 +#include "SkCondVar.h"
    1.15 +#include "SkRunnable.h"
    1.16 +#include "SkTDArray.h"
    1.17 +#include "SkTInternalLList.h"
    1.18 +#include "SkThreadUtils.h"
    1.19 +#include "SkTypes.h"
    1.20 +
    1.21 +#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
    1.22 +#    include <unistd.h>
    1.23 +#endif
    1.24 +
    1.25 +// Returns the number of cores on this machine.
    1.26 +static inline int num_cores() {
    1.27 +#if defined(SK_BUILD_FOR_WIN32)
    1.28 +    SYSTEM_INFO sysinfo;
    1.29 +    GetSystemInfo(&sysinfo);
    1.30 +    return sysinfo.dwNumberOfProcessors;
    1.31 +#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
    1.32 +    return sysconf(_SC_NPROCESSORS_ONLN);
    1.33 +#else
    1.34 +    return 1;
    1.35 +#endif
    1.36 +}
    1.37 +
    1.38 +template <typename T>
    1.39 +class SkTThreadPool {
    1.40 +public:
    1.41 +    /**
    1.42 +     * Create a threadpool with count threads, or one thread per core if kThreadPerCore.
    1.43 +     */
    1.44 +    static const int kThreadPerCore = -1;
    1.45 +    explicit SkTThreadPool(int count);
    1.46 +    ~SkTThreadPool();
    1.47 +
    1.48 +    /**
    1.49 +     * Queues up an SkRunnable to run when a thread is available, or synchronously if count is 0.
    1.50 +     * Does not take ownership. NULL is a safe no-op.  If T is not void, the runnable will be passed
    1.51 +     * a reference to a T on the thread's local stack.
    1.52 +     */
    1.53 +    void add(SkTRunnable<T>*);
    1.54 +
    1.55 +    /**
    1.56 +     * Block until all added SkRunnables have completed.  Once called, calling add() is undefined.
    1.57 +     */
    1.58 +    void wait();
    1.59 +
    1.60 + private:
    1.61 +    struct LinkedRunnable {
    1.62 +        SkTRunnable<T>* fRunnable;  // Unowned.
    1.63 +        SK_DECLARE_INTERNAL_LLIST_INTERFACE(LinkedRunnable);
    1.64 +    };
    1.65 +
    1.66 +    enum State {
    1.67 +        kRunning_State,  // Normal case.  We've been constructed and no one has called wait().
    1.68 +        kWaiting_State,  // wait has been called, but there still might be work to do or being done.
    1.69 +        kHalting_State,  // There's no work to do and no thread is busy.  All threads can shut down.
    1.70 +    };
    1.71 +
    1.72 +    SkTInternalLList<LinkedRunnable> fQueue;
    1.73 +    SkCondVar                        fReady;
    1.74 +    SkTDArray<SkThread*>             fThreads;
    1.75 +    State                            fState;
    1.76 +    int                              fBusyThreads;
    1.77 +
    1.78 +    static void Loop(void*);  // Static because we pass in this.
    1.79 +};
    1.80 +
    1.81 +template <typename T>
    1.82 +SkTThreadPool<T>::SkTThreadPool(int count) : fState(kRunning_State), fBusyThreads(0) {
    1.83 +    if (count < 0) {
    1.84 +        count = num_cores();
    1.85 +    }
    1.86 +    // Create count threads, all running SkTThreadPool::Loop.
    1.87 +    for (int i = 0; i < count; i++) {
    1.88 +        SkThread* thread = SkNEW_ARGS(SkThread, (&SkTThreadPool::Loop, this));
    1.89 +        *fThreads.append() = thread;
    1.90 +        thread->start();
    1.91 +    }
    1.92 +}
    1.93 +
    1.94 +template <typename T>
    1.95 +SkTThreadPool<T>::~SkTThreadPool() {
    1.96 +    if (kRunning_State == fState) {
    1.97 +        this->wait();
    1.98 +    }
    1.99 +}
   1.100 +
   1.101 +namespace SkThreadPoolPrivate {
   1.102 +
   1.103 +template <typename T>
   1.104 +struct ThreadLocal {
   1.105 +    void run(SkTRunnable<T>* r) { r->run(data); }
   1.106 +    T data;
   1.107 +};
   1.108 +
   1.109 +template <>
   1.110 +struct ThreadLocal<void> {
   1.111 +    void run(SkTRunnable<void>* r) { r->run(); }
   1.112 +};
   1.113 +
   1.114 +}  // namespace SkThreadPoolPrivate
   1.115 +
   1.116 +template <typename T>
   1.117 +void SkTThreadPool<T>::add(SkTRunnable<T>* r) {
   1.118 +    if (r == NULL) {
   1.119 +        return;
   1.120 +    }
   1.121 +
   1.122 +    if (fThreads.isEmpty()) {
   1.123 +        SkThreadPoolPrivate::ThreadLocal<T> threadLocal;
   1.124 +        threadLocal.run(r);
   1.125 +        return;
   1.126 +    }
   1.127 +
   1.128 +    LinkedRunnable* linkedRunnable = SkNEW(LinkedRunnable);
   1.129 +    linkedRunnable->fRunnable = r;
   1.130 +    fReady.lock();
   1.131 +    SkASSERT(fState != kHalting_State);  // Shouldn't be able to add work when we're halting.
   1.132 +    fQueue.addToHead(linkedRunnable);
   1.133 +    fReady.signal();
   1.134 +    fReady.unlock();
   1.135 +}
   1.136 +
   1.137 +
   1.138 +template <typename T>
   1.139 +void SkTThreadPool<T>::wait() {
   1.140 +    fReady.lock();
   1.141 +    fState = kWaiting_State;
   1.142 +    fReady.broadcast();
   1.143 +    fReady.unlock();
   1.144 +
   1.145 +    // Wait for all threads to stop.
   1.146 +    for (int i = 0; i < fThreads.count(); i++) {
   1.147 +        fThreads[i]->join();
   1.148 +        SkDELETE(fThreads[i]);
   1.149 +    }
   1.150 +    SkASSERT(fQueue.isEmpty());
   1.151 +}
   1.152 +
   1.153 +template <typename T>
   1.154 +/*static*/ void SkTThreadPool<T>::Loop(void* arg) {
   1.155 +    // The SkTThreadPool passes itself as arg to each thread as they're created.
   1.156 +    SkTThreadPool<T>* pool = static_cast<SkTThreadPool<T>*>(arg);
   1.157 +    SkThreadPoolPrivate::ThreadLocal<T> threadLocal;
   1.158 +
   1.159 +    while (true) {
   1.160 +        // We have to be holding the lock to read the queue and to call wait.
   1.161 +        pool->fReady.lock();
   1.162 +        while(pool->fQueue.isEmpty()) {
   1.163 +            // Does the client want to stop and are all the threads ready to stop?
   1.164 +            // If so, we move into the halting state, and whack all the threads so they notice.
   1.165 +            if (kWaiting_State == pool->fState && pool->fBusyThreads == 0) {
   1.166 +                pool->fState = kHalting_State;
   1.167 +                pool->fReady.broadcast();
   1.168 +            }
   1.169 +            // Any time we find ourselves in the halting state, it's quitting time.
   1.170 +            if (kHalting_State == pool->fState) {
   1.171 +                pool->fReady.unlock();
   1.172 +                return;
   1.173 +            }
   1.174 +            // wait yields the lock while waiting, but will have it again when awoken.
   1.175 +            pool->fReady.wait();
   1.176 +        }
   1.177 +        // We've got the lock back here, no matter if we ran wait or not.
   1.178 +
   1.179 +        // The queue is not empty, so we have something to run.  Claim it.
   1.180 +        LinkedRunnable* r = pool->fQueue.tail();
   1.181 +
   1.182 +        pool->fQueue.remove(r);
   1.183 +
   1.184 +        // Having claimed our SkRunnable, we now give up the lock while we run it.
   1.185 +        // Otherwise, we'd only ever do work on one thread at a time, which rather
   1.186 +        // defeats the point of this code.
   1.187 +        pool->fBusyThreads++;
   1.188 +        pool->fReady.unlock();
   1.189 +
   1.190 +        // OK, now really do the work.
   1.191 +        threadLocal.run(r->fRunnable);
   1.192 +        SkDELETE(r);
   1.193 +
   1.194 +        // Let everyone know we're not busy.
   1.195 +        pool->fReady.lock();
   1.196 +        pool->fBusyThreads--;
   1.197 +        pool->fReady.unlock();
   1.198 +    }
   1.199 +
   1.200 +    SkASSERT(false); // Unreachable.  The only exit happens when pool->fState is kHalting_State.
   1.201 +}
   1.202 +
   1.203 +typedef SkTThreadPool<void> SkThreadPool;
   1.204 +
   1.205 +#endif

mercurial