1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2017 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkExecutor.h" 9cb93a386Sopenharmony_ci#include "include/private/SkMutex.h" 10cb93a386Sopenharmony_ci#include "include/private/SkSemaphore.h" 11cb93a386Sopenharmony_ci#include "include/private/SkSpinlock.h" 12cb93a386Sopenharmony_ci#include "include/private/SkTArray.h" 13cb93a386Sopenharmony_ci#include <deque> 14cb93a386Sopenharmony_ci#include <thread> 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_WIN) 17cb93a386Sopenharmony_ci #include "src/core/SkLeanWindows.h" 18cb93a386Sopenharmony_ci static int num_cores() { 19cb93a386Sopenharmony_ci SYSTEM_INFO sysinfo; 20cb93a386Sopenharmony_ci GetNativeSystemInfo(&sysinfo); 21cb93a386Sopenharmony_ci return (int)sysinfo.dwNumberOfProcessors; 22cb93a386Sopenharmony_ci } 23cb93a386Sopenharmony_ci#else 24cb93a386Sopenharmony_ci #include <unistd.h> 25cb93a386Sopenharmony_ci static int num_cores() { 26cb93a386Sopenharmony_ci return (int)sysconf(_SC_NPROCESSORS_ONLN); 27cb93a386Sopenharmony_ci } 28cb93a386Sopenharmony_ci#endif 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_ciSkExecutor::~SkExecutor() {} 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ci// The default default SkExecutor is an SkTrivialExecutor, which just runs the work right away. 33cb93a386Sopenharmony_ciclass SkTrivialExecutor final : public SkExecutor { 34cb93a386Sopenharmony_ci void add(std::function<void(void)> work) override { 35cb93a386Sopenharmony_ci work(); 36cb93a386Sopenharmony_ci } 37cb93a386Sopenharmony_ci}; 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_cistatic SkExecutor& trivial_executor() { 40cb93a386Sopenharmony_ci static auto* executor = new SkTrivialExecutor(); 41cb93a386Sopenharmony_ci return *executor; 42cb93a386Sopenharmony_ci} 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_cistatic SkExecutor* gDefaultExecutor = nullptr; 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ciSkExecutor& SkExecutor::GetDefault() { 47cb93a386Sopenharmony_ci if (gDefaultExecutor) { 48cb93a386Sopenharmony_ci return *gDefaultExecutor; 49cb93a386Sopenharmony_ci } 50cb93a386Sopenharmony_ci return trivial_executor(); 51cb93a386Sopenharmony_ci} 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_civoid SkExecutor::SetDefault(SkExecutor* executor) { 54cb93a386Sopenharmony_ci gDefaultExecutor = executor; 55cb93a386Sopenharmony_ci} 56cb93a386Sopenharmony_ci 57cb93a386Sopenharmony_ci// We'll always push_back() new work, but pop from the front of deques or the back of SkTArray. 58cb93a386Sopenharmony_cistatic inline std::function<void(void)> pop(std::deque<std::function<void(void)>>* list) { 59cb93a386Sopenharmony_ci std::function<void(void)> fn = std::move(list->front()); 60cb93a386Sopenharmony_ci list->pop_front(); 61cb93a386Sopenharmony_ci return fn; 62cb93a386Sopenharmony_ci} 63cb93a386Sopenharmony_cistatic inline std::function<void(void)> pop(SkTArray<std::function<void(void)>>* list) { 64cb93a386Sopenharmony_ci std::function<void(void)> fn = std::move(list->back()); 65cb93a386Sopenharmony_ci list->pop_back(); 66cb93a386Sopenharmony_ci return fn; 67cb93a386Sopenharmony_ci} 68cb93a386Sopenharmony_ci 69cb93a386Sopenharmony_ci// An SkThreadPool is an executor that runs work on a fixed pool of OS threads. 70cb93a386Sopenharmony_citemplate <typename WorkList> 71cb93a386Sopenharmony_ciclass SkThreadPool final : public SkExecutor { 72cb93a386Sopenharmony_cipublic: 73cb93a386Sopenharmony_ci explicit SkThreadPool(int threads, bool allowBorrowing) : fAllowBorrowing(allowBorrowing) { 74cb93a386Sopenharmony_ci for (int i = 0; i < threads; i++) { 75cb93a386Sopenharmony_ci fThreads.emplace_back(&Loop, this); 76cb93a386Sopenharmony_ci } 77cb93a386Sopenharmony_ci } 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci ~SkThreadPool() override { 80cb93a386Sopenharmony_ci // Signal each thread that it's time to shut down. 81cb93a386Sopenharmony_ci for (int i = 0; i < fThreads.count(); i++) { 82cb93a386Sopenharmony_ci this->add(nullptr); 83cb93a386Sopenharmony_ci } 84cb93a386Sopenharmony_ci // Wait for each thread to shut down. 85cb93a386Sopenharmony_ci for (int i = 0; i < fThreads.count(); i++) { 86cb93a386Sopenharmony_ci fThreads[i].join(); 87cb93a386Sopenharmony_ci } 88cb93a386Sopenharmony_ci } 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci void add(std::function<void(void)> work) override { 91cb93a386Sopenharmony_ci // Add some work to our pile of work to do. 92cb93a386Sopenharmony_ci { 93cb93a386Sopenharmony_ci SkAutoMutexExclusive lock(fWorkLock); 94cb93a386Sopenharmony_ci fWork.emplace_back(std::move(work)); 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci // Tell the Loop() threads to pick it up. 97cb93a386Sopenharmony_ci fWorkAvailable.signal(1); 98cb93a386Sopenharmony_ci } 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_ci void borrow() override { 101cb93a386Sopenharmony_ci // If there is work waiting and we're allowed to borrow work, do it. 102cb93a386Sopenharmony_ci if (fAllowBorrowing && fWorkAvailable.try_wait()) { 103cb93a386Sopenharmony_ci SkAssertResult(this->do_work()); 104cb93a386Sopenharmony_ci } 105cb93a386Sopenharmony_ci } 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ciprivate: 108cb93a386Sopenharmony_ci // This method should be called only when fWorkAvailable indicates there's work to do. 109cb93a386Sopenharmony_ci bool do_work() { 110cb93a386Sopenharmony_ci std::function<void(void)> work; 111cb93a386Sopenharmony_ci { 112cb93a386Sopenharmony_ci SkAutoMutexExclusive lock(fWorkLock); 113cb93a386Sopenharmony_ci SkASSERT(!fWork.empty()); // TODO: if (fWork.empty()) { return true; } ? 114cb93a386Sopenharmony_ci work = pop(&fWork); 115cb93a386Sopenharmony_ci } 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci if (!work) { 118cb93a386Sopenharmony_ci return false; // This is Loop()'s signal to shut down. 119cb93a386Sopenharmony_ci } 120cb93a386Sopenharmony_ci 121cb93a386Sopenharmony_ci work(); 122cb93a386Sopenharmony_ci return true; 123cb93a386Sopenharmony_ci } 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci static void Loop(void* ctx) { 126cb93a386Sopenharmony_ci auto pool = (SkThreadPool*)ctx; 127cb93a386Sopenharmony_ci do { 128cb93a386Sopenharmony_ci pool->fWorkAvailable.wait(); 129cb93a386Sopenharmony_ci } while (pool->do_work()); 130cb93a386Sopenharmony_ci } 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci // Both SkMutex and SkSpinlock can work here. 133cb93a386Sopenharmony_ci using Lock = SkMutex; 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_ci SkTArray<std::thread> fThreads; 136cb93a386Sopenharmony_ci WorkList fWork; 137cb93a386Sopenharmony_ci Lock fWorkLock; 138cb93a386Sopenharmony_ci SkSemaphore fWorkAvailable; 139cb93a386Sopenharmony_ci bool fAllowBorrowing; 140cb93a386Sopenharmony_ci}; 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_cistd::unique_ptr<SkExecutor> SkExecutor::MakeFIFOThreadPool(int threads, bool allowBorrowing) { 143cb93a386Sopenharmony_ci using WorkList = std::deque<std::function<void(void)>>; 144cb93a386Sopenharmony_ci return std::make_unique<SkThreadPool<WorkList>>(threads > 0 ? threads : num_cores(), 145cb93a386Sopenharmony_ci allowBorrowing); 146cb93a386Sopenharmony_ci} 147cb93a386Sopenharmony_cistd::unique_ptr<SkExecutor> SkExecutor::MakeLIFOThreadPool(int threads, bool allowBorrowing) { 148cb93a386Sopenharmony_ci using WorkList = SkTArray<std::function<void(void)>>; 149cb93a386Sopenharmony_ci return std::make_unique<SkThreadPool<WorkList>>(threads > 0 ? threads : num_cores(), 150cb93a386Sopenharmony_ci allowBorrowing); 151cb93a386Sopenharmony_ci} 152