From 05cfb5cf7c50a66632186c4b751d3bd5a0db2c4a Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 24 Dec 2025 09:35:24 +0300 Subject: [PATCH 1/7] feat(ntp): update NTP time service singleton Refactor the NTP time service to use the eternal singleton pattern with optional DLL support. Add an ODR test target covering DLL singleton macros and adjust test setup. --- include/time_shield/ntp_time_service.hpp | 56 +++++++++++++++--------- tests/ntp_time_service_test.cpp | 1 - tests/odr/CMakeLists.txt | 11 +++++ tests/odr/ntp_time_service_dll_a.cpp | 22 ++++++++++ tests/odr/ntp_time_service_dll_b.cpp | 16 +++++++ 5 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 tests/odr/ntp_time_service_dll_a.cpp create mode 100644 tests/odr/ntp_time_service_dll_b.cpp diff --git a/include/time_shield/ntp_time_service.hpp b/include/time_shield/ntp_time_service.hpp index 57843459..3615f217 100644 --- a/include/time_shield/ntp_time_service.hpp +++ b/include/time_shield/ntp_time_service.hpp @@ -179,15 +179,42 @@ namespace time_shield { }; #endif // _TIME_SHIELD_TEST_FAKE_NTP -#ifndef TIME_SHIELD_CPP17 #if defined(TIME_SHIELD_TEST_FAKE_NTP) using RunnerAlias = detail::FakeNtpRunner; #else using RunnerAlias = NtpClientPoolRunner; #endif - extern NtpTimeServiceT g_ntp_time_service; -#endif // !TIME_SHIELD_CPP17 +#if defined(TIME_SHIELD_NTP_TIME_SERVICE_USE_DLL_SINGLETON) +#if defined(_WIN32) || defined(__CYGWIN__) +#ifdef TIME_SHIELD_NTP_TIME_SERVICE_DLL_EXPORTS +#define TIME_SHIELD_NTP_TIME_SERVICE_API __declspec(dllexport) +#else +#define TIME_SHIELD_NTP_TIME_SERVICE_API __declspec(dllimport) +#endif +#else +#define TIME_SHIELD_NTP_TIME_SERVICE_API +#endif + + extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& ntp_time_service_instance() noexcept; +#endif + + template + struct NtpTimeServiceSingleton final { + static NtpTimeServiceT& instance() noexcept { + static NtpTimeServiceT* p_instance = new NtpTimeServiceT{}; + return *p_instance; + } + }; + +#if defined(TIME_SHIELD_NTP_TIME_SERVICE_USE_DLL_SINGLETON) + template <> + struct NtpTimeServiceSingleton final { + static NtpTimeServiceT& instance() noexcept { + return ntp_time_service_instance(); + } + }; +#endif } // namespace detail /// \ingroup ntp @@ -202,11 +229,7 @@ namespace time_shield { /// \brief Return the singleton instance. /// \return Singleton instance. static NtpTimeServiceT& instance() noexcept { -#ifdef TIME_SHIELD_CPP17 - return m_instance; -#else - return detail::g_ntp_time_service; -#endif + return detail::NtpTimeServiceSingleton::instance(); } NtpTimeServiceT(const NtpTimeServiceT&) = delete; @@ -532,21 +555,14 @@ namespace time_shield { std::unique_ptr m_runner; -#ifdef TIME_SHIELD_CPP17 - static NtpTimeServiceT m_instance; -#endif }; -#ifdef TIME_SHIELD_CPP17 - template - inline NtpTimeServiceT NtpTimeServiceT::m_instance{}; -#endif - -#ifndef TIME_SHIELD_CPP17 +#if defined(TIME_SHIELD_NTP_TIME_SERVICE_USE_DLL_SINGLETON) && defined(TIME_SHIELD_NTP_TIME_SERVICE_DLL_IMPLEMENTATION) namespace detail { -#if defined(TIME_SHIELD_NTP_TIME_SERVICE_DEFINE) - NtpTimeServiceT g_ntp_time_service; -#endif + extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& ntp_time_service_instance() noexcept { + static NtpTimeServiceT* p_instance = new NtpTimeServiceT{}; + return *p_instance; + } } // namespace detail #endif diff --git a/tests/ntp_time_service_test.cpp b/tests/ntp_time_service_test.cpp index 2e197660..ee9bf149 100644 --- a/tests/ntp_time_service_test.cpp +++ b/tests/ntp_time_service_test.cpp @@ -2,7 +2,6 @@ #if TIME_SHIELD_ENABLE_NTP_CLIENT #define TIME_SHIELD_TEST_FAKE_NTP -#define TIME_SHIELD_NTP_TIME_SERVICE_DEFINE #include #include diff --git a/tests/odr/CMakeLists.txt b/tests/odr/CMakeLists.txt index f879e001..e526d626 100644 --- a/tests/odr/CMakeLists.txt +++ b/tests/odr/CMakeLists.txt @@ -14,3 +14,14 @@ target_link_libraries(odr_cxx17 PRIVATE time_shield::time_shield) set_target_properties(odr_cxx17 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) add_test(NAME odr_cxx17 COMMAND odr_cxx17) + +set(NTP_TIME_SERVICE_DLL_SOURCES + ntp_time_service_dll_a.cpp + ntp_time_service_dll_b.cpp +) + +add_executable(ntp_time_service_dll_odr ${NTP_TIME_SERVICE_DLL_SOURCES}) +target_link_libraries(ntp_time_service_dll_odr PRIVATE time_shield::time_shield) +set_target_properties(ntp_time_service_dll_odr PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES) + +add_test(NAME ntp_time_service_dll_odr COMMAND ntp_time_service_dll_odr) diff --git a/tests/odr/ntp_time_service_dll_a.cpp b/tests/odr/ntp_time_service_dll_a.cpp new file mode 100644 index 00000000..f431cbd6 --- /dev/null +++ b/tests/odr/ntp_time_service_dll_a.cpp @@ -0,0 +1,22 @@ +#include + +#if TIME_SHIELD_ENABLE_NTP_CLIENT +#define TIME_SHIELD_TEST_FAKE_NTP +#define TIME_SHIELD_NTP_TIME_SERVICE_USE_DLL_SINGLETON +#define TIME_SHIELD_NTP_TIME_SERVICE_DLL_EXPORTS +#define TIME_SHIELD_NTP_TIME_SERVICE_DLL_IMPLEMENTATION +#include + +int ntp_time_service_dll_helper(); + +int main() { + auto& service = time_shield::NtpTimeService::instance(); + service.shutdown(); + (void)ntp_time_service_dll_helper(); + return 0; +} +#else +int main() { + return 0; +} +#endif diff --git a/tests/odr/ntp_time_service_dll_b.cpp b/tests/odr/ntp_time_service_dll_b.cpp new file mode 100644 index 00000000..dcd903a7 --- /dev/null +++ b/tests/odr/ntp_time_service_dll_b.cpp @@ -0,0 +1,16 @@ +#include + +#if TIME_SHIELD_ENABLE_NTP_CLIENT +#define TIME_SHIELD_TEST_FAKE_NTP +#define TIME_SHIELD_NTP_TIME_SERVICE_USE_DLL_SINGLETON +#define TIME_SHIELD_NTP_TIME_SERVICE_DLL_EXPORTS +#include + +int ntp_time_service_dll_helper() { + return time_shield::NtpTimeService::instance().running() ? 1 : 0; +} +#else +int ntp_time_service_dll_helper() { + return 0; +} +#endif From 976adfce71bb6908414d2eb4a073832002dfc471 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 24 Dec 2025 10:15:22 +0300 Subject: [PATCH 2/7] fix(ntp): serialize NtpTimeService startup Add state and condition variable coordination to prevent concurrent runner starts. Ensure init, shutdown, and config reload wait for in-flight startup and cleanly roll back on failure. --- include/time_shield/ntp_time_service.hpp | 98 +++++++++++++++++++----- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/include/time_shield/ntp_time_service.hpp b/include/time_shield/ntp_time_service.hpp index 3615f217..1f224fcc 100644 --- a/include/time_shield/ntp_time_service.hpp +++ b/include/time_shield/ntp_time_service.hpp @@ -246,39 +246,62 @@ namespace time_shield { /// \param measure_immediately Measure before first sleep if true. /// \return True when background runner started. bool init(std::chrono::milliseconds interval, bool measure_immediately = true) { - std::unique_ptr local_runner; { - std::lock_guard lk(m_mtx); - if (is_running_locked()) { + std::unique_lock lk(m_mtx); + while (m_state == State::starting) { + m_cv.wait(lk); + } + if (m_state == State::running && is_running_locked()) { return true; } + m_state = State::starting; if (interval.count() <= 0) { interval = std::chrono::milliseconds(1); } m_interval = interval; m_measure_immediately = measure_immediately; + } + std::unique_ptr local_runner; + { + std::lock_guard lk(m_mtx); local_runner = build_runner_locked(); if (!local_runner) { + m_state = State::stopped; + m_cv.notify_all(); return false; } } - if (!local_runner->start(m_interval, m_measure_immediately)) { - return false; - } - bool is_ok = false; + bool has_started = false; try { - is_ok = local_runner->measure_now(); + has_started = local_runner->start(m_interval, m_measure_immediately); + if (has_started) { + is_ok = local_runner->measure_now(); + } } catch (...) { is_ok = false; } { std::lock_guard lk(m_mtx); - m_runner = std::move(local_runner); + if (!has_started) { + try { + local_runner->stop(); + } catch (...) { + } + local_runner.reset(); + } + if (has_started && is_ok) { + m_runner = std::move(local_runner); + m_state = State::running; + } else { + m_runner.reset(); + m_state = State::stopped; + } } + m_cv.notify_all(); return is_ok; } @@ -286,12 +309,17 @@ namespace time_shield { void shutdown() { std::unique_ptr local_runner; { - std::lock_guard lk(m_mtx); - if (!m_runner) { + std::unique_lock lk(m_mtx); + while (m_state == State::starting) { + m_cv.wait(lk); + } + if (m_state == State::stopped || !m_runner) { return; } + m_state = State::stopped; local_runner = std::move(m_runner); } + m_cv.notify_all(); try { local_runner->stop(); } catch (...) { @@ -303,7 +331,7 @@ namespace time_shield { /// \return True when background runner is active. bool running() const noexcept { std::lock_guard lk(m_mtx); - return is_running_locked(); + return m_state == State::running && is_running_locked(); } /// \brief Ensure background runner is started with current config. @@ -474,6 +502,15 @@ namespace time_shield { /// \brief Apply current config by rebuilding the runner. /// \return True when runner restarted successfully. bool apply_config_now() { + bool was_running = false; + { + std::unique_lock lk(m_mtx); + while (m_state == State::starting) { + m_cv.wait(lk); + } + was_running = m_state == State::running && is_running_locked(); + m_state = State::starting; + } std::unique_ptr new_runner; std::unique_ptr old_runner; std::chrono::milliseconds interval; @@ -482,6 +519,8 @@ namespace time_shield { std::lock_guard lk(m_mtx); new_runner = build_runner_locked(); if (!new_runner) { + m_state = was_running ? State::running : State::stopped; + m_cv.notify_all(); return false; } interval = m_interval; @@ -496,25 +535,46 @@ namespace time_shield { } } - if (!new_runner->start(interval, measure_immediately)) { - return false; - } - bool is_ok = false; + bool has_started = false; try { - is_ok = new_runner->measure_now(); + has_started = new_runner->start(interval, measure_immediately); + if (has_started) { + is_ok = new_runner->measure_now(); + } } catch (...) { is_ok = false; } { std::lock_guard lk(m_mtx); - m_runner = std::move(new_runner); + if (!has_started) { + try { + new_runner->stop(); + } catch (...) { + } + new_runner.reset(); + } + if (has_started && is_ok) { + m_runner = std::move(new_runner); + m_state = State::running; + } else { + m_runner.reset(); + m_state = State::stopped; + } } + m_cv.notify_all(); return is_ok; } private: + /// \brief Service state for coordination. + enum class State : uint8_t { + stopped, + starting, + running + }; + /// \brief Check runner status under lock. bool is_running_locked() const noexcept { return m_runner && m_runner->running(); @@ -544,6 +604,8 @@ namespace time_shield { private: mutable std::mutex m_mtx; + std::condition_variable m_cv; + State m_state{State::stopped}; std::chrono::milliseconds m_interval{std::chrono::seconds(30)}; bool m_measure_immediately{true}; From 6dc9ed24d4db6dc469d5cb6430166e1658b72bfd Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 24 Dec 2025 10:15:28 +0300 Subject: [PATCH 3/7] fix(ntp): cache offset for teardown-safe getters Register atexit shutdown for the eternal singleton and store a cached offset. Avoid implicit startup in time getters and update tests to start explicitly. --- include/time_shield/ntp_time_service.hpp | 46 ++++++++++++++++++++---- tests/ntp_time_service_test.cpp | 2 +- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/include/time_shield/ntp_time_service.hpp b/include/time_shield/ntp_time_service.hpp index 1f224fcc..dfd89dcc 100644 --- a/include/time_shield/ntp_time_service.hpp +++ b/include/time_shield/ntp_time_service.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -199,10 +200,34 @@ namespace time_shield { extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& ntp_time_service_instance() noexcept; #endif + template + NtpTimeServiceT*& shutdown_instance_ptr() noexcept { + static NtpTimeServiceT* instance_ptr = nullptr; + return instance_ptr; + } + + template + void shutdown_at_exit() noexcept { + auto* instance_ptr = shutdown_instance_ptr(); + if (!instance_ptr) { + return; + } + try { + instance_ptr->shutdown(); + } catch (...) { + } + } + template struct NtpTimeServiceSingleton final { static NtpTimeServiceT& instance() noexcept { static NtpTimeServiceT* p_instance = new NtpTimeServiceT{}; + static bool is_registered = []() noexcept { + shutdown_instance_ptr() = p_instance; + std::atexit(&shutdown_at_exit); + return true; + }(); + (void)is_registered; return *p_instance; } }; @@ -296,6 +321,7 @@ namespace time_shield { if (has_started && is_ok) { m_runner = std::move(local_runner); m_state = State::running; + m_last_offset_us.store(m_runner->offset_us()); } else { m_runner.reset(); m_state = State::stopped; @@ -345,19 +371,17 @@ namespace time_shield { /// \brief Return last estimated offset in microseconds. /// \return Offset in microseconds (UTC - local realtime). int64_t offset_us() noexcept { - ensure_started(); std::lock_guard lk(m_mtx); - if (!m_runner) return 0; - return m_runner->offset_us(); + if (m_runner) { + m_last_offset_us.store(m_runner->offset_us()); + } + return m_last_offset_us.load(); } /// \brief Return current UTC time in microseconds based on offset. /// \return UTC time in microseconds using last offset. int64_t utc_time_us() noexcept { - ensure_started(); - std::lock_guard lk(m_mtx); - if (!m_runner) return now_realtime_us(); - return m_runner->utc_time_us(); + return now_realtime_us() + offset_us(); } /// \brief Return current UTC time in milliseconds based on offset. @@ -558,6 +582,7 @@ namespace time_shield { if (has_started && is_ok) { m_runner = std::move(new_runner); m_state = State::running; + m_last_offset_us.store(m_runner->offset_us()); } else { m_runner.reset(); m_state = State::stopped; @@ -616,6 +641,7 @@ namespace time_shield { NtpPoolConfig m_pool_cfg{}; std::unique_ptr m_runner; + std::atomic m_last_offset_us{0}; }; @@ -623,6 +649,12 @@ namespace time_shield { namespace detail { extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& ntp_time_service_instance() noexcept { static NtpTimeServiceT* p_instance = new NtpTimeServiceT{}; + static bool is_registered = []() noexcept { + shutdown_instance_ptr() = p_instance; + std::atexit(&shutdown_at_exit); + return true; + }(); + (void)is_registered; return *p_instance; } } // namespace detail diff --git a/tests/ntp_time_service_test.cpp b/tests/ntp_time_service_test.cpp index ee9bf149..75652f58 100644 --- a/tests/ntp_time_service_test.cpp +++ b/tests/ntp_time_service_test.cpp @@ -15,7 +15,7 @@ int main() { service.shutdown(); assert(!service.running()); - (void)service.utc_time_ms(); + assert(service.init()); assert(service.running()); service.shutdown(); From d4354031b67d95041727fc35b634a23c2fa27c30 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 24 Dec 2025 10:15:33 +0300 Subject: [PATCH 4/7] fix(ntp): refine DLL singleton export naming Rename the exported DLL singleton symbol for NtpTimeService and document ABI expectations. Adjust stale age arithmetic and init return documentation; fix a macro comment typo. --- include/time_shield/ntp_time_service.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/time_shield/ntp_time_service.hpp b/include/time_shield/ntp_time_service.hpp index dfd89dcc..1aca0ac9 100644 --- a/include/time_shield/ntp_time_service.hpp +++ b/include/time_shield/ntp_time_service.hpp @@ -197,7 +197,9 @@ namespace time_shield { #define TIME_SHIELD_NTP_TIME_SERVICE_API #endif - extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& ntp_time_service_instance() noexcept; + /// \brief Return the process-wide singleton instance from a DLL export. + /// \note All modules must use the same configuration macros to keep ABI consistent. + extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& time_shield_ntp_time_service_instance() noexcept; #endif template @@ -236,7 +238,7 @@ namespace time_shield { template <> struct NtpTimeServiceSingleton final { static NtpTimeServiceT& instance() noexcept { - return ntp_time_service_instance(); + return time_shield_ntp_time_service_instance(); } }; #endif @@ -261,7 +263,7 @@ namespace time_shield { NtpTimeServiceT& operator=(const NtpTimeServiceT&) = delete; /// \brief Start background measurements using stored interval. - /// \return True when background runner started. + /// \return True when background runner started and initial measurement succeeded. bool init() { return init(m_interval, m_measure_immediately); } @@ -445,7 +447,7 @@ namespace time_shield { return true; } const int64_t age = now_realtime_us() - last; - return age > max_age.count() * 1000; + return age > static_cast(max_age.count()) * 1000; } /// \brief Replace server list used for new runner instances. @@ -647,7 +649,7 @@ namespace time_shield { #if defined(TIME_SHIELD_NTP_TIME_SERVICE_USE_DLL_SINGLETON) && defined(TIME_SHIELD_NTP_TIME_SERVICE_DLL_IMPLEMENTATION) namespace detail { - extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& ntp_time_service_instance() noexcept { + extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& time_shield_ntp_time_service_instance() noexcept { static NtpTimeServiceT* p_instance = new NtpTimeServiceT{}; static bool is_registered = []() noexcept { shutdown_instance_ptr() = p_instance; @@ -785,6 +787,6 @@ namespace time_shield { }; } // namespace time_shield -#endif // _TIME_SHIELD_ENABLE_NTP_CLIENT +#endif // TIME_SHIELD_ENABLE_NTP_CLIENT #endif // _TIME_SHIELD_NTP_TIME_SERVICE_HPP_INCLUDED From f7f3ee3948c8953defdcf3180e48db46b0735fe1 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 24 Dec 2025 10:26:39 +0300 Subject: [PATCH 5/7] fix(ntp): refine startup notifications Move condition variable notifications out of locked sections and add a cached offset fast path. Document DLL shutdown guidance and note UTC fallback behavior. --- include/time_shield/ntp_time_service.hpp | 37 +++++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/include/time_shield/ntp_time_service.hpp b/include/time_shield/ntp_time_service.hpp index 1aca0ac9..a8e4aa08 100644 --- a/include/time_shield/ntp_time_service.hpp +++ b/include/time_shield/ntp_time_service.hpp @@ -199,6 +199,7 @@ namespace time_shield { /// \brief Return the process-wide singleton instance from a DLL export. /// \note All modules must use the same configuration macros to keep ABI consistent. + /// \note Call ntp::shutdown() explicitly when unloading a DLL or plugin. extern "C" TIME_SHIELD_NTP_TIME_SERVICE_API NtpTimeServiceT& time_shield_ntp_time_service_instance() noexcept; #endif @@ -271,8 +272,9 @@ namespace time_shield { /// \brief Start background measurements with interval and immediate flag. /// \param interval Measurement interval. /// \param measure_immediately Measure before first sleep if true. - /// \return True when background runner started. + /// \return True when background runner started and initial measurement succeeded. bool init(std::chrono::milliseconds interval, bool measure_immediately = true) { + bool should_notify = false; { std::unique_lock lk(m_mtx); while (m_state == State::starting) { @@ -295,9 +297,14 @@ namespace time_shield { local_runner = build_runner_locked(); if (!local_runner) { m_state = State::stopped; + should_notify = true; + } + } + if (!local_runner) { + if (should_notify) { m_cv.notify_all(); - return false; } + return false; } bool is_ok = false; @@ -329,7 +336,10 @@ namespace time_shield { m_state = State::stopped; } } - m_cv.notify_all(); + should_notify = true; + if (should_notify) { + m_cv.notify_all(); + } return is_ok; } @@ -373,14 +383,16 @@ namespace time_shield { /// \brief Return last estimated offset in microseconds. /// \return Offset in microseconds (UTC - local realtime). int64_t offset_us() noexcept { + const int64_t cached = m_last_offset_us.load(); std::lock_guard lk(m_mtx); if (m_runner) { m_last_offset_us.store(m_runner->offset_us()); } - return m_last_offset_us.load(); + return m_runner ? m_last_offset_us.load() : cached; } /// \brief Return current UTC time in microseconds based on offset. + /// \note Returns realtime time when the service has never been started. /// \return UTC time in microseconds using last offset. int64_t utc_time_us() noexcept { return now_realtime_us() + offset_us(); @@ -529,12 +541,13 @@ namespace time_shield { /// \return True when runner restarted successfully. bool apply_config_now() { bool was_running = false; + bool should_notify = false; { std::unique_lock lk(m_mtx); while (m_state == State::starting) { m_cv.wait(lk); } - was_running = m_state == State::running && is_running_locked(); + was_running = m_runner && is_running_locked(); m_state = State::starting; } std::unique_ptr new_runner; @@ -546,13 +559,18 @@ namespace time_shield { new_runner = build_runner_locked(); if (!new_runner) { m_state = was_running ? State::running : State::stopped; - m_cv.notify_all(); - return false; + should_notify = true; } interval = m_interval; measure_immediately = m_measure_immediately; old_runner = std::move(m_runner); } + if (!new_runner) { + if (should_notify) { + m_cv.notify_all(); + } + return false; + } if (old_runner) { try { @@ -590,7 +608,10 @@ namespace time_shield { m_state = State::stopped; } } - m_cv.notify_all(); + should_notify = true; + if (should_notify) { + m_cv.notify_all(); + } return is_ok; } From aea856dcb46996257952a0e98798431288a11648 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 24 Dec 2025 11:42:27 +0300 Subject: [PATCH 6/7] fix(ntp): tighten offset cache and config apply Simplify init notifications, streamline offset_us caching, and prevent apply_config_now from clearing a running runner when build fails. --- include/time_shield/ntp_time_service.hpp | 33 +++++++----------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/include/time_shield/ntp_time_service.hpp b/include/time_shield/ntp_time_service.hpp index a8e4aa08..04ac0fe9 100644 --- a/include/time_shield/ntp_time_service.hpp +++ b/include/time_shield/ntp_time_service.hpp @@ -274,7 +274,6 @@ namespace time_shield { /// \param measure_immediately Measure before first sleep if true. /// \return True when background runner started and initial measurement succeeded. bool init(std::chrono::milliseconds interval, bool measure_immediately = true) { - bool should_notify = false; { std::unique_lock lk(m_mtx); while (m_state == State::starting) { @@ -297,14 +296,9 @@ namespace time_shield { local_runner = build_runner_locked(); if (!local_runner) { m_state = State::stopped; - should_notify = true; - } - } - if (!local_runner) { - if (should_notify) { m_cv.notify_all(); + return false; } - return false; } bool is_ok = false; @@ -336,10 +330,7 @@ namespace time_shield { m_state = State::stopped; } } - should_notify = true; - if (should_notify) { - m_cv.notify_all(); - } + m_cv.notify_all(); return is_ok; } @@ -383,12 +374,14 @@ namespace time_shield { /// \brief Return last estimated offset in microseconds. /// \return Offset in microseconds (UTC - local realtime). int64_t offset_us() noexcept { - const int64_t cached = m_last_offset_us.load(); + const int64_t cached = m_last_offset_us.load(std::memory_order_relaxed); std::lock_guard lk(m_mtx); - if (m_runner) { - m_last_offset_us.store(m_runner->offset_us()); + if (!m_runner) { + return cached; } - return m_runner ? m_last_offset_us.load() : cached; + const int64_t current = m_runner->offset_us(); + m_last_offset_us.store(current, std::memory_order_relaxed); + return current; } /// \brief Return current UTC time in microseconds based on offset. @@ -541,7 +534,6 @@ namespace time_shield { /// \return True when runner restarted successfully. bool apply_config_now() { bool was_running = false; - bool should_notify = false; { std::unique_lock lk(m_mtx); while (m_state == State::starting) { @@ -559,18 +551,13 @@ namespace time_shield { new_runner = build_runner_locked(); if (!new_runner) { m_state = was_running ? State::running : State::stopped; - should_notify = true; + m_cv.notify_all(); + return false; } interval = m_interval; measure_immediately = m_measure_immediately; old_runner = std::move(m_runner); } - if (!new_runner) { - if (should_notify) { - m_cv.notify_all(); - } - return false; - } if (old_runner) { try { From 7b7884bf9942016d368a78a4f4136eb6a9392617 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Wed, 24 Dec 2025 13:11:24 +0300 Subject: [PATCH 7/7] fix(ntp): remove leftover notify flag Drop an unused should_notify assignment in apply_config_now and keep a single post-update notification. --- include/time_shield/ntp_time_service.hpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/time_shield/ntp_time_service.hpp b/include/time_shield/ntp_time_service.hpp index 04ac0fe9..74e14e1e 100644 --- a/include/time_shield/ntp_time_service.hpp +++ b/include/time_shield/ntp_time_service.hpp @@ -595,10 +595,7 @@ namespace time_shield { m_state = State::stopped; } } - should_notify = true; - if (should_notify) { - m_cv.notify_all(); - } + m_cv.notify_all(); return is_ok; }