OGLplus  (0.59.0) a C++ wrapper for rendering APIs

workshop.hpp
Go to the documentation of this file.
1 
9 #ifndef EAGINE_WORKSHOP_HPP
10 #define EAGINE_WORKSHOP_HPP
11 
12 #include "branch_predict.hpp"
13 #include "extract.hpp"
14 #include "integer_range.hpp"
15 #include "interface.hpp"
16 #include "types.hpp"
17 #include <condition_variable>
18 #include <memory>
19 #include <mutex>
20 #include <queue>
21 #include <thread>
22 #include <tuple>
23 #include <vector>
24 
25 namespace eagine {
26 //------------------------------------------------------------------------------
27 struct work_unit : interface<work_unit> {
28  virtual auto do_it() -> bool = 0;
29  virtual void deliver() = 0;
30 };
31 //------------------------------------------------------------------------------
32 class workshop {
33 private:
34  std::vector<std::thread> _workers{};
35  std::mutex _mutex{};
36  std::condition_variable _cond{};
37  std::queue<work_unit*> _work_queue{};
38  bool _shutdown{false};
39 
40  auto _fetch() -> std::tuple<work_unit*, bool> {
41  std::unique_lock lock{_mutex};
42  work_unit* work = nullptr;
43  if(!_shutdown) {
44  _cond.wait(
45  lock, [this] { return !_work_queue.empty() || _shutdown; });
46  if(!_work_queue.empty()) {
47  work = _work_queue.front();
48  _work_queue.pop();
49  }
50  }
51  return {work, _shutdown};
52  }
53 
54  void _employ() {
55  while(true) {
56  auto [opt_work, shutdown] = _fetch();
57  if(opt_work) {
58  auto& work = extract(opt_work);
59  if(work.do_it()) {
60  std::unique_lock lock{_mutex};
61  work.deliver();
62  _cond.notify_all();
63  } else {
64  enqueue(work);
65  }
66  }
67  if(shutdown) {
68  break;
69  }
70  }
71  }
72 
73 public:
74  workshop() = default;
75  workshop(workshop&&) = delete;
76  workshop(const workshop&) = delete;
77  auto operator=(workshop&&) = delete;
78  auto operator=(const workshop&) = delete;
79  ~workshop() noexcept {
80  try {
81  shutdown();
82  wait_until_closed();
83  } catch(...) {
84  }
85  }
86 
87  auto shutdown() -> workshop& {
88  std::unique_lock lock{_mutex};
89  _shutdown = true;
90  _cond.notify_all();
91  return *this;
92  }
93 
94  auto wait_until_closed() -> workshop& {
95  for(auto& worker : _workers) {
96  worker.join();
97  }
98  return *this;
99  }
100 
101  auto wait_until_idle() -> workshop& {
102  std::unique_lock lock{_mutex};
103  _cond.wait(lock, [this]() { return _work_queue.empty(); });
104  return *this;
105  }
106 
107  auto add_worker() -> workshop& {
108  _workers.emplace_back([this]() { this->_employ(); });
109  return *this;
110  }
111 
112  auto add_workers(span_size_t n) -> workshop& {
113  _workers.reserve(_workers.size() + std_size(n));
114  for(auto i : integer_range(n)) {
115  EAGINE_MAYBE_UNUSED(i);
116  add_worker();
117  }
118  return *this;
119  }
120 
121  auto ensure_workers(span_size_t n) -> workshop& {
122  const auto c = span_size(_workers.size());
123  if(n > c) {
124  add_workers(n - c);
125  }
126  return *this;
127  }
128 
129  auto populate() -> workshop& {
130  return ensure_workers(span_size(std::thread::hardware_concurrency()));
131  }
132 
133  auto release_worker() -> workshop& {
134  _workers.pop_back();
135  return *this;
136  }
137 
138  auto enqueue(work_unit& work) -> workshop& {
139  std::unique_lock lock{_mutex};
140  if(EAGINE_UNLIKELY(_workers.empty())) {
141  add_worker();
142  }
143  _work_queue.push(&work);
144  _cond.notify_one();
145  return *this;
146  }
147 };
148 //------------------------------------------------------------------------------
149 } // namespace eagine
150 
151 #endif // EAGINE_WORKSHOP_HPP
std::ptrdiff_t span_size_t
Signed span size type used by eagine.
Definition: types.hpp:36
static constexpr auto span_size(T v) noexcept
Converts argument to span size type.
Definition: types.hpp:59
Common code is placed in this namespace.
Definition: eagine.hpp:21
static constexpr auto extract(api_result_value< Result, api_result_validity::never > &) noexcept -> Result &
Overload of extract for api_result_value.
Definition: c_api_wrap.hpp:270
static constexpr auto std_size(T v) noexcept
Converts argument to std size type.
Definition: types.hpp:52
integer_range(B, E) -> integer_range< std::common_type_t< B, E >>
Deduction guide for integer_range.

Copyright © 2015-2021 Matúš Chochlík.
<chochlik -at -gmail.com>
Documentation generated on Tue Apr 13 2021 by Doxygen (version 1.8.17).