Home | History | Annotate | Download | only in core
      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>
     18     static int num_cores() {
     19         SYSTEM_INFO sysinfo;
     20         GetNativeSystemInfo(&sysinfo);
     21         return (int)sysinfo.dwNumberOfProcessors;
     22     }
     23 #else
     24     #include <unistd.h>
     25     static int num_cores() {
     26         return (int)sysconf(_SC_NPROCESSORS_ONLN);
     27     }
     28 #endif
     29 
     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 {
     34     void add(std::function<void(void)> work) override {
     35         work();
     36     }
     37 };
     38 
     39 static SkTrivialExecutor gTrivial;
     40 static SkExecutor* gDefaultExecutor = &gTrivial;
     41 
     42 SkExecutor& SkExecutor::GetDefault() {
     43     return *gDefaultExecutor;
     44 }
     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:
     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 
     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 
     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 
     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.
     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 
    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 
    122 std::unique_ptr<SkExecutor> SkExecutor::MakeThreadPool(int threads) {
    123     return skstd::make_unique<SkThreadPool>(threads > 0 ? threads : num_cores());
    124 }
    125