From 862c6e7c92f4d23567ec1580713b1b84e6e196c1 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Tue, 28 Oct 2025 18:45:18 -0700 Subject: [PATCH] (FIX): Simplified the watchdog. Now it runs the way its expected to, just needs to be wired up into the parser somehow. --- lib/watchdog.cpp | 148 ++++++++++++++++++----------------------------- lib/watchdog.h | 146 ++++++++++++++++++++++++---------------------- src/main.cpp | 12 ---- 3 files changed, 132 insertions(+), 174 deletions(-) diff --git a/lib/watchdog.cpp b/lib/watchdog.cpp index 85aa520..06f3eac 100644 --- a/lib/watchdog.cpp +++ b/lib/watchdog.cpp @@ -1,114 +1,76 @@ #include "watchdog.h" +#include +#include +#include -namespace fs = std::filesystem; // makes it easier to read +namespace fs = std::filesystem; +using namespace std::chrono; void Watchdog::Start() { // checks if file exist - if (!fs::exists(path)) { - // returns and sets parameters to false if file doesnt exist - this->watching = false; - this->has_initial_time = false; - std::cout << "Watchdog: File does not exists: " << path << std::endl; - return; + if (!fs::exists(this->path)) + throw std::runtime_error("File does not exist."); + + // Loop forever and check the file. + while (true) { + CheckFile(); + std::this_thread::sleep_for(this->POLLING_INTERVAL); } - // grabs intial write time - this->last_write_time = fs::last_write_time(path); - this->watching = true; - this->has_initial_time = true; - std::cout << "Watchdog: Started" << std::endl; } -bool Watchdog::CheckFile() { - // If not watching returns false - if (!watching) - return false; +void Watchdog::CheckFile() { + // LONG STORY SHORT: + // When a file is written to, it aquires an OS lock, which means our program + // cannot access it. So when we request, it fails. So we should basically just + // keep trying until we get access. + // + // After implementing some retry logic, I was unable to replicate the error. - try { - // Checking if file was deleted - if (!fs::exists(path)) { - if (this->has_initial_time) { - std::cout << "Watchdog: File was delete: " << path << std::endl; - this->has_initial_time = false; - return true; + for (int attempt = 0; attempt < this->MAX_RETRIES; ++attempt) { + try { + // Checking if file was deleted + if (!fs::exists(this->path)) + throw std::runtime_error( + "File could not be found. Maybe it was deleted?"); + + // Built in function with file system to check last write time + fs::file_time_type currentWriteTime = fs::last_write_time(path); + + // File modified + if (currentWriteTime != this->last_write_time) { + this->last_write_time = currentWriteTime; + + time_point before = high_resolution_clock::now(); + + // DO SOMETHING + std::this_thread::sleep_for(this->POLLING_INTERVAL); + + time_point after = high_resolution_clock::now(); + + duration dur = after - before; + long ms = std::chrono::duration_cast(dur).count(); + std::cout << std::endl + << "Recompiled in \033[36m" << ms << "ms\033[0m" << std::endl; } - return false; - } + } catch (const std::exception &ex) { + // On last attempt, bubble the error outward + if (attempt == this->MAX_RETRIES - 1) + throw std::runtime_error("Watchdog failed after multiple retries: " + + std::string(ex.what())); - // Built in function with file system to check last write tim - fs::file_time_type currentWriteTime = fs::last_write_time(path); - - // File was just created - if (!this->has_initial_time) { - this->last_write_time = currentWriteTime; - this->has_initial_time = true; - std::cout << "Watchdog: File created: " << path << std::endl; - return true; + // Wait and then try again + std::this_thread::sleep_for(this->RETRY_DELAY); } - - // File modified - if (currentWriteTime != this->last_write_time) { - this->last_write_time = currentWriteTime; - std::cout << "Watchdog: File modifed at " - << TimePointToString(this->last_write_time) << std::endl; - return true; - } - } catch (const fs::filesystem_error &e) { - // File deleted, inaccessible, or path invalid - if (this->has_initial_time) { - std::cout << "Watchdog: File deleted or inaccessible: " << path - << std::endl; - this->has_initial_time = false; - return true; - } - return false; - } catch (const std::exception &e) { - std::cerr << "Watchdog: Unexpected error checking file: " << e.what() - << std::endl; - return false; } - - // No change - return false; } std::string Watchdog::TimePointToString(const fs::file_time_type &timePoint) { - /** - * Step 1: timePoint - * - This is the last write time of the file, returned by std::filesystem. - * - Its clock is platform-dependent (filesystem clock). - * - * Step 2: fs::file_time_type::clock::now() - * - Current time according to the filesystem clock. - * - * Step 3: std::chrono::system_clock::now() - * - Current time according to the system clock (standard C++ clock). - * - * Conversion formula: - * timePoint - fs::file_time_type::clock::now() + - * std::chrono::system_clock::now() - * - * Explanation: - * a) timePoint - fs::file_time_type::clock::now() - * - Calculates the duration between the file's last write time and "now" - * according to the filesystem clock. - * b) + std::chrono::system_clock::now() - * - Shifts that duration to align with the system clock timeline. - * c) std::chrono::time_point_cast(...) - * - Ensures the resulting time_point uses the correct duration type - * for std::chrono::system_clock. - * - * Result: - * - systemTime is a std::chrono::system_clock::time_point representing - * the same instant as timePoint, but compatible with system_clock. - */ - std::chrono::system_clock::time_point systemTimePoint = - std::chrono::time_point_cast( - timePoint - fs::file_time_type::clock::now() + - std::chrono::system_clock::now()); + system_clock::time_point systemTimePoint = + time_point_cast( + timePoint - fs::file_time_type::clock::now() + system_clock::now()); // Converts to seconds - std::time_t timeInSeconds = - std::chrono::system_clock::to_time_t(systemTimePoint); + std::time_t timeInSeconds = system_clock::to_time_t(systemTimePoint); // Converts to local time, built in function std::tm localTime = *std::localtime(&timeInSeconds); diff --git a/lib/watchdog.h b/lib/watchdog.h index 3164ac1..32b0aef 100644 --- a/lib/watchdog.h +++ b/lib/watchdog.h @@ -1,24 +1,23 @@ -/** +/** * @file watchdog.h * @brief file watcher * @author Preston Shultz - * Sources: + * Sources: * https://en.cppreference.com/w/cpp/filesystem.html * https://en.cppreference.com/w/cpp/chrono.html * Format the time into a string using strftime - * https://en.cppreference.com/w/cpp/chrono/c/strftime.html + * https://en.cppreference.com/w/cpp/chrono/c/strftime.html */ #ifndef WATCHDOG_H #define WATCHDOG_H -#include -#include +#include //makes timestamps easier +#include //Convert time_t to std::tm for local time #include //allow access to files platform independent -#include //makes timestamps easier -#include //Convert time_t to std::tm for local time -#include //Formats std::tim into "YYYY-MM-DD HH-MM-SS" - +#include //Formats std::tim into "YYYY-MM-DD HH-MM-SS" +#include +#include /** * @brief watchdog class. @@ -27,70 +26,79 @@ * * @author Preston Shultz (shultzp1@my.erau.edu) */ -class Watchdog{ +class Watchdog { +protected: + /** + * @brief watchdog class. + * + * Checks if a file has been modified + * + * @author Preston Shultz (shultzp1@my.erau.edu) + * @author Hayden Hargreaves (hhargreaves2006@gmail.com) + */ + void CheckFile(); + public: - Watchdog(const std::string& path) : - path(path), watching(false), has_initial_time(false) {} + Watchdog(const std::string &path) : path(path) {} - /** - * @brief watchdog class. - * - * Starts the watchdog to check of a file is modified - * - * @author Preston Shultz (shultzp1@my.erau.edu) - */ - void Start(); - /** - * @brief watchdog class. - * - * Stops the watchdog - * - * @author Preston Shultz (shultzp1@my.erau.edu) - */ - void Stop() {watching = false;} //Disable + /** + * @brief watchdog class. + * + * Starts the watchdog to check of a file is modified + * + * @author Preston Shultz (shultzp1@my.erau.edu) + */ + void Start(); - /** - * @brief watchdog class. - * - * Checks if a file has been modified - * - * @author Preston Shultz (shultzp1@my.erau.edu) - */ - bool CheckFile(); - - /** - * @brief watchdog class. - * - * Returns files last modified date - * - * @author Preston Shultz (shultzp1@my.erau.edu) - */ - std::filesystem::file_time_type GetLastWriteTime() - const {return last_write_time;} - - /** - * @brief watchdog class. - * - * Converts time point into a string - * - * @author Preston Shultz (shultzp1@my.erau.edu) - */ - static std::string TimePointToString(const std::filesystem::file_time_type& timePoint); - - /** - * @brief watchdog class. - * - * Returns if the watchDog is on - * - * @author Preston Shultz (shultzp1@my.erau.edu) - */ - bool IsWatching() const{return watching;} + /** + * @brief watchdog class. + * + * Converts time point into a string + * + * Details below: + * + * Step 1: timePoint + * - This is the last write time of the file, returned by std::filesystem. + * - Its clock is platform-dependent (filesystem clock). + * + * Step 2: fs::file_time_type::clock::now() + * - Current time according to the filesystem clock. + * + * Step 3: std::chrono::system_clock::now() + * - Current time according to the system clock (standard C++ clock). + * + * Conversion formula: + * timePoint - fs::file_time_type::clock::now() + + * std::chrono::system_clock::now() + * + * Explanation: + * a) timePoint - fs::file_time_type::clock::now() + * - Calculates the duration between the file's last write time and "now" + * according to the filesystem clock. + * b) + std::chrono::system_clock::now() + * - Shifts that duration to align with the system clock timeline. + * c) std::chrono::time_point_cast(...) + * - Ensures the resulting time_point uses the correct duration type + * for std::chrono::system_clock. + * + * Result: + * - systemTime is a std::chrono::system_clock::time_point representing + * the same instant as timePoint, but compatible with system_clock. + * + * @author Preston Shultz (shultzp1@my.erau.edu) + */ + static std::string + TimePointToString(const std::filesystem::file_time_type &timePoint); private: - std::string path; //file path - bool watching; //Is watchdog on? - std::filesystem::file_time_type last_write_time; - bool has_initial_time; //checks if initial time is given + std::string path; + std::filesystem::file_time_type last_write_time; + + // Timing values for the watchdog + const std::chrono::milliseconds POLLING_INTERVAL = + std::chrono::milliseconds(100); + const int MAX_RETRIES = 20; + const std::chrono::milliseconds RETRY_DELAY = std::chrono::milliseconds(20); }; -#endif \ No newline at end of file +#endif diff --git a/src/main.cpp b/src/main.cpp index c5e2626..922ba50 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,18 +41,6 @@ void test_nodes() { void test_watchdog() { Watchdog wd("test/input.md"); wd.Start(); - - std::cout << "Initial check (should do nothing if file unchanged):\n"; - wd.CheckFile(); - - std::cout << "Now, modify or create the file 'example.txt' manually and " - "press Enter:\n"; - std::cin.get(); // Wait for user to press Enter - - // Check again after manual change - wd.CheckFile(); - - std::cout << "Done testing.\n"; } void test_input(int argc, char **argv) {