File threadloop.hpp
File List > illixr > threadloop.hpp
Go to the documentation of this file
#pragma once
#include "cpu_timer.hpp"
#include "error_util.hpp"
#include "phonebook.hpp"
#include "plugin.hpp"
#include "record_logger.hpp"
#include "stoplight.hpp"
#include
#include
#include
#include
#include
#include
#include
namespace ILLIXR {
const record_header _threadloop_iteration_header{
"threadloop_iteration",
{
{"plugin_id", typeid(std::size_t)},
{"iteration_no", typeid(std::size_t)},
{"skips", typeid(std::size_t)},
{"cpu_time_start", typeid(std::chrono::nanoseconds)},
{"cpu_time_stop", typeid(std::chrono::nanoseconds)},
{"wall_time_start", typeid(std::chrono::high_resolution_clock::time_point)},
{"wall_time_stop", typeid(std::chrono::high_resolution_clock::time_point)},
}};
class threadloop : public plugin {
public:
threadloop(const std::string& name, phonebook* pb)
: plugin{name, pb}
, stoplight_{pb->lookup_impl<stoplight>()} { }
void start() override {
plugin::start();
thread_ = std::thread([this] {
thread_main();
});
assert(!stoplight_->check_should_stop());
assert(thread_.joinable());
}
void stop() override {
assert(stoplight_->check_should_stop());
// only join if it has been started
if (thread_.joinable())
thread_.join();
plugin::stop();
}
virtual void internal_stop() {
internal_stop_.store(true);
}
~threadloop() override {
assert(!thread_.joinable() || stoplight_->check_should_stop());
assert(!thread_.joinable());
}
protected:
enum class skip_option {
run,
skip_and_spin,
skip_and_yield,
stop,
};
virtual skip_option _p_should_skip() {
return skip_option::run;
}
virtual void _p_thread_setup() { }
virtual void _p_one_iteration() = 0;
bool should_terminate() {
return internal_stop_.load();
}
std::size_t iteration_no = 0;
std::size_t skip_no = 0;
private:
void thread_main() {
record_coalescer it_log{record_logger_};
// TODO: In the future, synchronize the main loop instead of the setup.
// This is currently not possible because relative_clock is required in
// some setup functions, and relative_clock is only guaranteed to be
// available once `wait_for_ready()` unblocks.
stoplight_->wait_for_ready();
_p_thread_setup();
while (!stoplight_->check_should_stop() && !should_terminate()) {
skip_option s = _p_should_skip();
switch (s) {
case skip_option::skip_and_yield:
std::this_thread::yield();
++skip_no;
break;
case skip_option::skip_and_spin:
++skip_no;
break;
case skip_option::run: {
auto iteration_start_cpu_time = thread_cpu_time();
auto iteration_start_wall_time = std::chrono::high_resolution_clock::now();
RAC_ERRNO();
_p_one_iteration();
RAC_ERRNO();
it_log.log(record{_threadloop_iteration_header,
{
{id_},
{iteration_no},
{skip_no},
{iteration_start_cpu_time},
{thread_cpu_time()},
{iteration_start_wall_time},
{std::chrono::high_resolution_clock::now()},
}});
++iteration_no;
skip_no = 0;
break;
}
case skip_option::stop:
// Break out of the switch AND the loop
// See https://stackoverflow.com/questions/27788326/breaking-out-of-nested-loop-c
goto break_loop;
}
}
break_loop:
[[maybe_unused]] int cpp_requires_a_statement_after_a_label_plz_optimize_me_away;
}
std::atomic<bool> internal_stop_{false};
std::thread thread_;
std::shared_ptr<const stoplight> stoplight_;
};
} // namespace ILLIXR