Skip to content

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