• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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 #include "SkExecutor.h"
9 #include "SkMakeUnique.h"
10 #include "SkMutex.h"
11 #include "SkSemaphore.h"
12 #include "SkSpinlock.h"
13 #include "SkTArray.h"
14 #include "SkThreadUtils.h"
15 
16 #if defined(SK_BUILD_FOR_WIN32)
17     #include <windows.h>
num_cores()18     static int num_cores() {
19         SYSTEM_INFO sysinfo;
20         GetNativeSystemInfo(&sysinfo);
21         return (int)sysinfo.dwNumberOfProcessors;
22     }
23 #else
24     #include <unistd.h>
num_cores()25     static int num_cores() {
26         return (int)sysconf(_SC_NPROCESSORS_ONLN);
27     }
28 #endif
29 
~SkExecutor()30 SkExecutor::~SkExecutor() {}
31 
32 // The default default SkExecutor is an SkTrivialExecutor, which just runs the work right away.
33 class SkTrivialExecutor final : public SkExecutor {
add(std::function<void (void)> work)34     void add(std::function<void(void)> work) override {
35         work();
36     }
37 };
38 
39 static SkTrivialExecutor gTrivial;
40 static SkExecutor* gDefaultExecutor = &gTrivial;
41 
GetDefault()42 SkExecutor& SkExecutor::GetDefault() {
43     return *gDefaultExecutor;
44 }
SetDefault(SkExecutor * executor)45 void SkExecutor::SetDefault(SkExecutor* executor) {
46     gDefaultExecutor = executor ? executor : &gTrivial;
47 }
48 
49 // An SkThreadPool is an executor that runs work on a fixed pool of OS threads.
50 class SkThreadPool final : public SkExecutor {
51 public:
SkThreadPool(int threads)52     explicit SkThreadPool(int threads) {
53         for (int i = 0; i < threads; i++) {
54             fThreads.emplace_back(new SkThread(&Loop, this));
55             fThreads.back()->start();
56         }
57     }
58 
~SkThreadPool()59     ~SkThreadPool() override {
60         // Signal each thread that it's time to shut down.
61         for (int i = 0; i < fThreads.count(); i++) {
62             this->add(nullptr);
63         }
64         // Wait for each thread to shut down.
65         for (int i = 0; i < fThreads.count(); i++) {
66             fThreads[i]->join();
67         }
68     }
69 
add(std::function<void (void)> work)70     virtual void add(std::function<void(void)> work) override {
71         // Add some work to our pile of work to do.
72         {
73             SkAutoExclusive lock(fWorkLock);
74             fWork.emplace_back(std::move(work));
75         }
76         // Tell the Loop() threads to pick it up.
77         fWorkAvailable.signal(1);
78     }
79 
borrow()80     virtual void borrow() override {
81         // If there is work waiting, do it.
82         if (fWorkAvailable.try_wait()) {
83             SkAssertResult(this->do_work());
84         }
85     }
86 
87 private:
88     // This method should be called only when fWorkAvailable indicates there's work to do.
do_work()89     bool do_work() {
90         std::function<void(void)> work;
91         {
92             SkAutoExclusive lock(fWorkLock);
93             SkASSERT(!fWork.empty());        // TODO: if (fWork.empty()) { return true; } ?
94             work = std::move(fWork.back());
95             fWork.pop_back();
96         }
97 
98         if (!work) {
99             return false;  // This is Loop()'s signal to shut down.
100         }
101 
102         work();
103         return true;
104     }
105 
Loop(void * ctx)106     static void Loop(void* ctx) {
107         auto pool = (SkThreadPool*)ctx;
108         do {
109             pool->fWorkAvailable.wait();
110         } while (pool->do_work());
111     }
112 
113     // Both SkMutex and SkSpinlock can work here.
114     using Lock = SkMutex;
115 
116     SkTArray<std::unique_ptr<SkThread>> fThreads;
117     SkTArray<std::function<void(void)>> fWork;
118     Lock                                fWorkLock;
119     SkSemaphore                         fWorkAvailable;
120 };
121 
MakeThreadPool(int threads)122 std::unique_ptr<SkExecutor> SkExecutor::MakeThreadPool(int threads) {
123     return skstd::make_unique<SkThreadPool>(threads > 0 ? threads : num_cores());
124 }
125