aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/utils/SkThreadPool.cpp
blob: 5fe10255b5ae599945cd4fa3c5051a35dcdb05f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkRunnable.h"
#include "SkThreadPool.h"
#include "SkThreadUtils.h"
#include "SkTypes.h"

#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
#include <unistd.h>
#endif

// Returns the number of cores on this machine.
static int num_cores() {
#if defined(SK_BUILD_FOR_WIN32)
    SYSTEM_INFO sysinfo;
    GetSystemInfo(&sysinfo);
    return sysinfo.dwNumberOfProcessors;
#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
    return sysconf(_SC_NPROCESSORS_ONLN);
#else
    return 1;
#endif
}

SkThreadPool::SkThreadPool(int count)
: fDone(false) {
    if (count < 0) count = num_cores();
    // Create count threads, all running SkThreadPool::Loop.
    for (int i = 0; i < count; i++) {
        SkThread* thread = SkNEW_ARGS(SkThread, (&SkThreadPool::Loop, this));
        *fThreads.append() = thread;
        thread->start();
    }
}

SkThreadPool::~SkThreadPool() {
    fDone = true;
    fReady.lock();
    fReady.broadcast();
    fReady.unlock();

    // Wait for all threads to stop.
    for (int i = 0; i < fThreads.count(); i++) {
        fThreads[i]->join();
        SkDELETE(fThreads[i]);
    }
}

/*static*/ void SkThreadPool::Loop(void* arg) {
    // The SkThreadPool passes itself as arg to each thread as they're created.
    SkThreadPool* pool = static_cast<SkThreadPool*>(arg);

    while (true) {
        // We have to be holding the lock to read the queue and to call wait.
        pool->fReady.lock();
        while(pool->fQueue.isEmpty()) {
            // Is it time to die?
            if (pool->fDone) {
                pool->fReady.unlock();
                return;
            }
            // wait yields the lock while waiting, but will have it again when awoken.
            pool->fReady.wait();
        }
        // We've got the lock back here, no matter if we ran wait or not.

        // The queue is not empty, so we have something to run.  Claim it.
        LinkedRunnable* r = pool->fQueue.tail();

        pool->fQueue.remove(r);

        // Having claimed our SkRunnable, we now give up the lock while we run it.
        // Otherwise, we'd only ever do work on one thread at a time, which rather
        // defeats the point of this code.
        pool->fReady.unlock();

        // OK, now really do the work.
        r->fRunnable->run();
        SkDELETE(r);
    }

    SkASSERT(false); // Unreachable.  The only exit happens when pool->fDone.
}

void SkThreadPool::add(SkRunnable* r) {
    if (NULL == r) {
        return;
    }

    // If we don't have any threads, obligingly just run the thing now.
    if (fThreads.isEmpty()) {
        return r->run();
    }

    // We have some threads.  Queue it up!
    fReady.lock();
    LinkedRunnable* linkedRunnable = SkNEW(LinkedRunnable);
    linkedRunnable->fRunnable = r;
    fQueue.addToHead(linkedRunnable);
    fReady.signal();
    fReady.unlock();
}