diff --git a/examples/simple_example/main.cc b/examples/simple_example/main.cc index fc08685..7900acf 100644 --- a/examples/simple_example/main.cc +++ b/examples/simple_example/main.cc @@ -7,48 +7,89 @@ using cactus_rt::App; using cactus_rt::CyclicThread; /** - * This is a no-op thread that does nothing at 1 kHz. + * This is a thread that loops at 1 kHz. We always inherit from CyclicThread if + * we want a thread that executes periodically. + * + * In this thread, we need to make sure we construct the cactus_rt::CyclicThread + * object as a part of the constructor to initialize the thread. To write code + * that executes at 1000 Hz, override and implement the Loop() function. + * + * This class demonstrates how it is done. */ class ExampleRTThread : public CyclicThread { - int64_t loop_counter_ = 0; - public: - ExampleRTThread(const char* name, cactus_rt::CyclicThreadConfig config) : CyclicThread(name, config) {} - - int64_t GetLoopCounter() const { - return loop_counter_; - } + /** + * @brief Construct the thread. Note we are initializing the creating the + * CyclicThread here with a predetermined name and config (via MakeConfig()). + */ + ExampleRTThread() : CyclicThread("ExampleRTThread", MakeConfig()) {} protected: - bool Loop(int64_t /*now*/) noexcept final { - loop_counter_++; - if (loop_counter_ % 1000 == 0) { - LOG_INFO(Logger(), "Loop {}", loop_counter_); - } + /** + * @brief This methods runs every loop, which for this particular example is every 1ms. + * + * @param elapsed_ns The number of nanoseconds elapsed since the App::Start was called. + * @return true if you want the thread to stop + * @return false if you want to thread to continue + */ + bool Loop(int64_t elapsed_ns) noexcept final { + // Code written in this function executes every 1 ms. + + // This demonstrates the usage of the quill logger. This emits a log message every 1s. + LOG_INFO_LIMIT(std::chrono::seconds(1), Logger(), "Looping for {}", std::chrono::nanoseconds(elapsed_ns)); return false; } + + private: + /** + * @brief Creates a CyclicThreadConfig object which configures this thread. + */ + static cactus_rt::CyclicThreadConfig MakeConfig() { + // This function sets up the thread configuration for this thread. + cactus_rt::CyclicThreadConfig config; + + // Run at 1000 Hz. + config.period_ns = 1'000'000; + + // Pin this thread on CPU core #2. If you don't want to pin in on a CPU core, + // remove the following line. + config.cpu_affinity = std::vector{2}; + + // Run the thread with SCHED_FIFO at real-time priority of 80. + config.SetFifoScheduler(80); + return config; + } }; int main() { - cactus_rt::CyclicThreadConfig config; - config.period_ns = 1'000'000; - config.cpu_affinity = std::vector{2}; - config.SetFifoScheduler(80); + // We first create cactus_rt App object. + App app; - auto thread = std::make_shared("ExampleRTThread", config); + // We then create a thread object + auto thread = std::make_shared(); - App app; - app.StartTraceSession("build/data.perfetto"); + // We then register the thread with the app, which allows the app to start, + // stop, and join the thread via App::Start, App::RequestStop, and App::Join. app.RegisterThread(thread); constexpr unsigned int time = 5; std::cout << "Testing RT loop for " << time << " seconds.\n"; + // Start the application, which starts all the registered threads (any thread + // passed to App::RegisterThread) in the order they are registered. app.Start(); + + // We let the application run for 5 seconds. std::this_thread::sleep_for(std::chrono::seconds(time)); + + // We ask the application to stop, which stops all registered threads in the + // order they are registered. app.RequestStop(); + + // We wait until all threads registered are done here. app.Join(); - std::cout << "Number of loops executed: " << thread->GetLoopCounter() << "\n"; + std::cout << "Done\n"; + return 0; }