ILLIXR: Illinois Extended Reality Testbed
threadloop.hpp
1 #pragma once
2 
3 #include "cpu_timer.hpp"
4 #include "error_util.hpp"
5 #include "phonebook.hpp"
6 #include "plugin.hpp"
7 #include "record_logger.hpp"
8 #include "stoplight.hpp"
9 
10 #include <atomic>
11 #include <cassert>
12 #include <chrono>
13 #include <functional>
14 #include <iostream>
15 #include <memory>
16 #include <thread>
17 
18 namespace ILLIXR {
19 
20 const record_header __threadloop_iteration_header{
21  "threadloop_iteration",
22  {
23  {"plugin_id", typeid(std::size_t)},
24  {"iteration_no", typeid(std::size_t)},
25  {"skips", typeid(std::size_t)},
26  {"cpu_time_start", typeid(std::chrono::nanoseconds)},
27  {"cpu_time_stop", typeid(std::chrono::nanoseconds)},
28  {"wall_time_start", typeid(std::chrono::high_resolution_clock::time_point)},
29  {"wall_time_stop", typeid(std::chrono::high_resolution_clock::time_point)},
30  }};
31 
39 class threadloop : public plugin {
40 public:
41  threadloop(const std::string& name_, phonebook* pb_)
42  : plugin{name_, pb_}
43  , _m_stoplight{pb->lookup_impl<Stoplight>()} { }
44 
58  void start() override {
59  plugin::start();
60  _m_thread = std::thread(std::bind(&threadloop::thread_main, this));
61  assert(!_m_stoplight->check_should_stop());
62  assert(_m_thread.joinable());
63  }
64 
70  void stop() override {
71  assert(_m_stoplight->check_should_stop());
72  assert(_m_thread.joinable());
73  _m_thread.join();
74  }
75 
81  virtual void internal_stop() {
82  _m_internal_stop.store(true);
83  }
84 
85  ~threadloop() override {
86  assert(_m_stoplight->check_should_stop());
87  assert(!_m_thread.joinable());
88  }
89 
90 protected:
91  std::size_t iteration_no = 0;
92  std::size_t skip_no = 0;
93 
94 private:
95  void thread_main() {
96  record_coalescer it_log{record_logger_};
97 
98  // TODO: In the future, synchronize the main loop instead of the setup.
99  // This is currently not possible because RelativeClock is required in
100  // some setup functions, and RelativeClock is only guaranteed to be
101  // available once `wait_for_ready()` unblocks.
102  _m_stoplight->wait_for_ready();
103  _p_thread_setup();
104 
105  while (!_m_stoplight->check_should_stop() && !should_terminate()) {
107 
108  switch (s) {
110  std::this_thread::yield();
111  ++skip_no;
112  break;
114  ++skip_no;
115  break;
116  case skip_option::run: {
117  auto iteration_start_cpu_time = thread_cpu_time();
118  auto iteration_start_wall_time = std::chrono::high_resolution_clock::now();
119 
120  RAC_ERRNO();
122  RAC_ERRNO();
123 
124  it_log.log(record{__threadloop_iteration_header,
125  {
126  {id},
127  {iteration_no},
128  {skip_no},
129  {iteration_start_cpu_time},
130  {thread_cpu_time()},
131  {iteration_start_wall_time},
132  {std::chrono::high_resolution_clock::now()},
133  }});
134  ++iteration_no;
135  skip_no = 0;
136  break;
137  }
138  case skip_option::stop:
139  // Break out of the switch AND the loop
140  // See https://stackoverflow.com/questions/27788326/breaking-out-of-nested-loop-c
141  goto break_loop;
142  }
143  }
144  break_loop:
145  [[maybe_unused]] int cpp_requires_a_statement_after_a_label_plz_optimize_me_away;
146  }
147 
148 protected:
149  enum class skip_option {
151  run,
152 
155 
159 
161  stop,
162  };
163 
168  return skip_option::run;
169  }
170 
174  virtual void _p_thread_setup() { }
175 
181  virtual void _p_one_iteration() = 0;
182 
189  return _m_internal_stop.load();
190  }
191 
192 private:
193  std::atomic<bool> _m_internal_stop{false};
194  std::thread _m_thread;
195  std::shared_ptr<const Stoplight> _m_stoplight;
196 };
197 
198 } // namespace ILLIXR
Start/stop synchronization for the whole application.
Definition: stoplight.hpp:106
A service locator for ILLIXR.
Definition: phonebook.hpp:68
std::shared_ptr< specific_service > lookup_impl() const
Look up an implementation of specific_service, which should be registered first.
Definition: phonebook.hpp:122
A dynamically-loadable plugin for Spindle.
Definition: plugin.hpp:32
virtual void start()
A method which Spindle calls when it starts the component.
Definition: plugin.hpp:41
A reusable threadloop for plugins.
Definition: threadloop.hpp:39
virtual void _p_thread_setup()
Gets called at setup time, from the new thread.
Definition: threadloop.hpp:174
skip_option
Definition: threadloop.hpp:149
@ run
Run iteration NOW. Only then does CPU timer begin counting.
@ skip_and_spin
AKA "busy wait". Skip but try again very quickly.
virtual void internal_stop()
Stops the thread.
Definition: threadloop.hpp:81
virtual void _p_one_iteration()=0
Override with the computation the thread does every loop.
bool should_terminate()
Whether the thread has been asked to terminate.
Definition: threadloop.hpp:188
void stop() override
Joins the thread.
Definition: threadloop.hpp:70
virtual skip_option _p_should_skip()
Gets called in a tight loop, to gate the invocation of _p_one_iteration()
Definition: threadloop.hpp:167
void start() override
Starts the thread.
Definition: threadloop.hpp:58
RAC_ERRNO_MSG.
Definition: data_format.hpp:15