SkExecutor.cpp 4.4 KB

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