blob: e078af3ba3e914add1428460b66c24f6dafe9bb6 [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
scroggo@google.com4177ef42012-10-31 15:52:16 +00008#include "SkRunnable.h"
commit-bot@chromium.org44c661f2013-04-22 15:23:14 +00009#include "SkThreadPool.h"
scroggo@google.com4177ef42012-10-31 15:52:16 +000010#include "SkThreadUtils.h"
commit-bot@chromium.org44c661f2013-04-22 15:23:14 +000011#include "SkTypes.h"
scroggo@google.com4177ef42012-10-31 15:52:16 +000012
commit-bot@chromium.org44c661f2013-04-22 15:23:14 +000013#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
14#include <unistd.h>
15#endif
16
17// Returns the number of cores on this machine.
18static int num_cores() {
19#if defined(SK_BUILD_FOR_WIN32)
20 SYSTEM_INFO sysinfo;
21 GetSystemInfo(&sysinfo);
22 return sysinfo.dwNumberOfProcessors;
23#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_ANDROID)
24 return sysconf(_SC_NPROCESSORS_ONLN);
25#else
26 return 1;
27#endif
28}
29
30SkThreadPool::SkThreadPool(int count)
scroggo@google.com4177ef42012-10-31 15:52:16 +000031: fDone(false) {
commit-bot@chromium.org44c661f2013-04-22 15:23:14 +000032 if (count < 0) count = num_cores();
scroggo@google.com4177ef42012-10-31 15:52:16 +000033 // Create count threads, all running SkThreadPool::Loop.
34 for (int i = 0; i < count; i++) {
35 SkThread* thread = SkNEW_ARGS(SkThread, (&SkThreadPool::Loop, this));
36 *fThreads.append() = thread;
37 thread->start();
38 }
39}
40
41SkThreadPool::~SkThreadPool() {
commit-bot@chromium.orga7538ba2013-10-10 18:49:04 +000042 if (!fDone) {
43 this->wait();
44 }
45}
46
47void SkThreadPool::wait() {
scroggo@google.com4177ef42012-10-31 15:52:16 +000048 fReady.lock();
commit-bot@chromium.orgedf23672013-10-01 18:44:18 +000049 fDone = true;
scroggo@google.com4177ef42012-10-31 15:52:16 +000050 fReady.broadcast();
51 fReady.unlock();
52
53 // Wait for all threads to stop.
54 for (int i = 0; i < fThreads.count(); i++) {
55 fThreads[i]->join();
56 SkDELETE(fThreads[i]);
57 }
58}
59
60/*static*/ void SkThreadPool::Loop(void* arg) {
61 // The SkThreadPool passes itself as arg to each thread as they're created.
62 SkThreadPool* pool = static_cast<SkThreadPool*>(arg);
63
64 while (true) {
65 // We have to be holding the lock to read the queue and to call wait.
66 pool->fReady.lock();
67 while(pool->fQueue.isEmpty()) {
68 // Is it time to die?
69 if (pool->fDone) {
70 pool->fReady.unlock();
71 return;
72 }
73 // wait yields the lock while waiting, but will have it again when awoken.
74 pool->fReady.wait();
75 }
76 // We've got the lock back here, no matter if we ran wait or not.
77
78 // The queue is not empty, so we have something to run. Claim it.
79 LinkedRunnable* r = pool->fQueue.tail();
80
81 pool->fQueue.remove(r);
82
83 // Having claimed our SkRunnable, we now give up the lock while we run it.
84 // Otherwise, we'd only ever do work on one thread at a time, which rather
85 // defeats the point of this code.
86 pool->fReady.unlock();
87
88 // OK, now really do the work.
89 r->fRunnable->run();
90 SkDELETE(r);
91 }
92
93 SkASSERT(false); // Unreachable. The only exit happens when pool->fDone.
94}
95
96void SkThreadPool::add(SkRunnable* r) {
97 if (NULL == r) {
98 return;
99 }
100
101 // If we don't have any threads, obligingly just run the thing now.
102 if (fThreads.isEmpty()) {
103 return r->run();
104 }
105
106 // We have some threads. Queue it up!
107 fReady.lock();
commit-bot@chromium.orga7538ba2013-10-10 18:49:04 +0000108 SkASSERT(!fDone); // We shouldn't be adding work to a pool that's shut down.
scroggo@google.com4177ef42012-10-31 15:52:16 +0000109 LinkedRunnable* linkedRunnable = SkNEW(LinkedRunnable);
110 linkedRunnable->fRunnable = r;
111 fQueue.addToHead(linkedRunnable);
112 fReady.signal();
113 fReady.unlock();
114}