3#ifndef _TIME_SHIELD_NTP_TIME_SERVICE_HPP_INCLUDED
4#define _TIME_SHIELD_NTP_TIME_SERVICE_HPP_INCLUDED
8#if TIME_SHIELD_ENABLE_NTP_CLIENT
16#include <condition_variable>
27 template <
class RunnerT>
31 template <
class RunnerT>
34 template <
class RunnerT>
37#ifdef TIME_SHIELD_TEST_FAKE_NTP
55 bool start(std::chrono::milliseconds interval = std::chrono::seconds(30),
56 bool measure_immediately =
true) {
60 if (interval.count() <= 0) {
61 interval = std::chrono::milliseconds(1);
78 bool start(
int interval_ms,
bool measure_immediately =
true) {
79 return start(std::chrono::milliseconds(interval_ms), measure_immediately);
143 std::unique_lock<std::mutex> lk(
m_cv_mtx);
144 m_cv.wait_for(lk, sleep_interval, [
this]() {
160 m_offset_us.store(
static_cast<int64_t
>(count * 1000));
169 std::chrono::milliseconds
m_interval{std::chrono::seconds(30)};
198 template <
class RunnerT>
223 bool init(std::chrono::milliseconds interval,
bool measure_immediately =
true) {
228 std::unique_ptr<RunnerT> local_runner;
229 std::chrono::milliseconds start_interval = interval;
230 bool use_measure_immediately = measure_immediately;
232 std::unique_lock<std::mutex> lk(
m_mtx);
242 if (start_interval.count() <= 0) {
243 start_interval = std::chrono::milliseconds(1);
258 bool has_started =
false;
261 has_started = local_runner->start(start_interval, use_measure_immediately);
263 is_ok = local_runner->measure_now();
272 local_runner->stop();
277 local_runner.reset();
281 std::lock_guard<std::mutex> lk(
m_mtx);
283 m_last_offset_us.store(local_runner->offset_us(), std::memory_order_relaxed);
299 bool init(
int interval_ms,
bool measure_immediately =
true) {
300 return init(std::chrono::milliseconds(interval_ms), measure_immediately);
305 std::unique_ptr<RunnerT> local_runner;
307 std::unique_lock<std::mutex> lk(
m_mtx);
318 local_runner->stop();
323 std::lock_guard<std::mutex> lk(
m_mtx);
332 std::lock_guard<std::mutex> lk(
m_mtx);
356 std::lock_guard<std::mutex> lk(
m_mtx);
358 const int64_t offset =
m_runner->offset_us();
372 std::lock_guard<std::mutex> lk(
m_mtx);
393 std::lock_guard<std::mutex> lk(
m_mtx);
401 std::lock_guard<std::mutex> lk(
m_mtx);
409 std::lock_guard<std::mutex> lk(
m_mtx);
417 std::lock_guard<std::mutex> lk(
m_mtx);
419 return m_runner->last_update_realtime_us();
425 std::lock_guard<std::mutex> lk(
m_mtx);
427 return m_runner->last_success_realtime_us();
433 bool stale(std::chrono::milliseconds max_age)
const noexcept {
439 return age >
static_cast<int64_t
>(max_age.count()) * 1000;
445 bool stale(
int max_age_ms)
const noexcept {
446 return stale(std::chrono::milliseconds(max_age_ms));
453 std::lock_guard<std::mutex> lk(
m_mtx);
465 std::lock_guard<std::mutex> lk(
m_mtx);
477 std::lock_guard<std::mutex> lk(
m_mtx);
490 std::lock_guard<std::mutex> lk(
m_mtx);
502 std::lock_guard<std::mutex> lk(
m_mtx);
512 std::lock_guard<std::mutex> lk(
m_mtx);
529 std::unique_ptr<RunnerT> new_runner;
530 std::unique_ptr<RunnerT> old_runner;
531 std::chrono::milliseconds interval;
532 bool measure_immediately =
true;
533 bool was_running =
false;
535 std::unique_lock<std::mutex> lk(
m_mtx);
563 bool has_started =
false;
566 has_started = new_runner->start(interval, measure_immediately);
568 is_ok = new_runner->measure_now();
586 std::lock_guard<std::mutex> lk(
m_mtx);
626 std::unique_ptr<RunnerT> local_runner;
628 std::unique_lock<std::mutex> lk(
m_mtx);
643 local_runner->stop();
650 std::lock_guard<std::mutex> lk(
m_mtx);
688 std::vector<NtpServerConfig> servers;
697 pool.set_servers(std::move(servers));
699 std::unique_ptr<RunnerT> runner;
701 runner.reset(
new RunnerT(std::move(pool)));
715 std::chrono::milliseconds
m_interval{std::chrono::seconds(30)};
728 template <
class RunnerT>
740 instance().begin_process_shutdown();
744 template <
class RunnerT>
760#if defined(TIME_SHIELD_TEST_FAKE_NTP)
777 inline bool init(std::chrono::milliseconds interval = std::chrono::seconds(30),
778 bool measure_immediately =
true) {
787 inline bool init(
int interval_ms,
788 bool measure_immediately =
true) {
865 inline bool stale(std::chrono::milliseconds max_age)
noexcept {
873 inline bool stale(
int max_age_ms)
noexcept {
887 static_assert(
sizeof(
void*) == 0,
"NtpTimeService is disabled by configuration.");
static std::vector< NtpServerConfig > build_default_servers()
Singleton service for background NTP measurements.
std::unique_ptr< RunnerT > build_runner_locked()
Build a runner with current server list and pool config.
std::atomic< int64_t > m_last_offset_us
NtpTimeServiceT()=default
Construct service.
NtpTimeServiceT(const NtpTimeServiceT &)=delete
uint64_t measure_count() const noexcept
Return total number of measurement attempts.
bool is_running_locked() const noexcept
Check runner status under lock.
bool is_process_shutting_down_locked() const noexcept
Return true when process shutdown has started.
uint32_t atexit_registration_count() const noexcept
Return number of successful atexit registrations.
bool init(int interval_ms, bool measure_immediately=true)
Start background measurements using milliseconds.
int64_t last_success_realtime_us() const noexcept
Return realtime timestamp of last successful measurement.
NtpPoolConfig pool_config() const
Return current pool configuration.
State
Lifecycle state of the singleton runner.
void shutdown()
Stop background measurements and release resources.
bool clear_servers()
Clear custom server list and return to default behavior.
int64_t utc_time_sec() noexcept
Return current UTC time in seconds based on offset.
bool init()
Start background measurements using stored interval.
ProcessState
Global lifetime state of the immortal singleton.
std::atomic< ProcessState > m_process_state
bool is_reconfigurable_locked() const noexcept
Return true when configuration can be changed safely.
bool set_pool_config(NtpPoolConfig cfg)
Override pool configuration for new runner instances.
NtpTimeServiceT & operator=(const NtpTimeServiceT &)=delete
std::condition_variable m_cv
bool running() const noexcept
Return true when background runner is active.
bool stale(std::chrono::milliseconds max_age) const noexcept
Return true when last measurement is older than max_age.
int64_t utc_time_ms() noexcept
Return current UTC time in milliseconds based on offset.
bool m_has_custom_pool_cfg
static NtpTimeServiceT & instance() noexcept
Return the singleton instance.
bool m_has_custom_servers
bool last_measure_ok() const noexcept
Return whether last measurement updated the offset.
std::vector< NtpServerConfig > m_servers
void register_process_shutdown_handler() noexcept
Register one process-shutdown handler for this specialization.
bool set_servers(std::vector< NtpServerConfig > servers)
Replace server list used for new runner instances.
~NtpTimeServiceT()=default
Immortal singleton is stopped via process-shutdown handler.
bool stale(int max_age_ms) const noexcept
Return true when last measurement is older than max_age_ms.
int64_t offset_us() noexcept
Return last estimated offset in microseconds.
uint64_t fail_count() const noexcept
Return number of failed measurement attempts.
bool is_transitioning_locked() const noexcept
Return true when a start or stop transition is in progress.
bool m_measure_immediately
bool set_default_servers()
Use conservative default servers for new runner instances.
bool apply_config_now()
Apply current config by rebuilding the runner.
void begin_process_shutdown() noexcept
Mark the singleton as shutting down and stop the runner.
std::unique_ptr< detail::FakeNtpRunner > m_runner
int64_t utc_time_us() noexcept
Return current UTC time in microseconds based on offset.
bool init(std::chrono::milliseconds interval, bool measure_immediately=true)
Start background measurements with interval and immediate flag.
std::atomic< uint32_t > m_atexit_registration_count
std::vector< NtpSample > last_samples() const
Return copy of last measurement samples.
std::chrono::milliseconds m_interval
void ensure_started() noexcept
Ensure background runner is started with current config.
bool is_process_shutting_down() const noexcept
Return true when process shutdown has started.
int64_t last_update_realtime_us() const noexcept
Return realtime timestamp of last measurement attempt.
static NtpTimeService & instance()
std::atomic< bool > m_is_stop_requested
int64_t last_update_realtime_us() const noexcept
Return realtime timestamp of last measurement attempt.
int64_t utc_time_sec() const noexcept
Return current UTC time in seconds based on offset.
std::atomic< int64_t > m_last_success_realtime_us
FakeNtpRunner()=default
Construct fake runner.
int64_t utc_time_us() const noexcept
Return current UTC time in microseconds based on offset.
FakeNtpRunner(const FakeNtpRunner &)=delete
std::atomic< bool > m_last_measure_ok
std::atomic< bool > m_is_force_requested
bool last_measure_ok() const noexcept
Return whether last measurement updated the offset.
int64_t last_success_realtime_us() const noexcept
Return realtime timestamp of last successful measurement.
bool force_measure()
Wake the worker thread and request a measurement.
bool running() const noexcept
Return true when background thread is running.
uint64_t fail_count() const noexcept
Return number of failed measurement attempts.
std::atomic< int64_t > m_last_update_realtime_us
bool start(std::chrono::milliseconds interval=std::chrono::seconds(30), bool measure_immediately=true)
Start fake measurements on a background thread.
int64_t offset_us() const noexcept
Return last estimated offset in microseconds.
bool do_measure()
Update fake offset and stats.
std::atomic< uint64_t > m_measure_count
void run_loop()
Background loop for fake measurements.
int64_t utc_time_ms() const noexcept
Return current UTC time in milliseconds based on offset.
bool m_measure_immediately
std::chrono::milliseconds m_interval
std::atomic< int64_t > m_offset_us
std::atomic< bool > m_is_running
bool measure_now()
Perform one measurement immediately.
~FakeNtpRunner()
Stop background thread on destruction.
uint64_t measure_count() const noexcept
Return total number of measurement attempts.
bool start(int interval_ms, bool measure_immediately=true)
Start fake measurements using milliseconds.
void stop()
Stop background measurements.
FakeNtpRunner(NtpClientPool)
Construct fake runner with an unused pool.
FakeNtpRunner & operator=(const FakeNtpRunner &)=delete
std::vector< NtpSample > last_samples() const
Return copy of most recent samples.
std::atomic< uint64_t > m_fail_count
std::condition_variable m_cv
Configuration macros for the library.
void init()
Initializes the Time Shield library.
int64_t utc_time_sec() noexcept
Return current UTC time in seconds based on offset.
uint64_t measure_count() noexcept
Return total number of measurement attempts.
bool stale(std::chrono::milliseconds max_age) noexcept
Return true when last measurement is older than max_age.
bool last_measure_ok() noexcept
Return whether last measurement updated the offset.
int64_t offset_us() noexcept
Return last estimated offset in microseconds.
void shutdown()
Stop NTP time service.
int64_t utc_time_ms() noexcept
Return current UTC time in milliseconds based on offset.
int64_t last_success_realtime_us() noexcept
Return realtime timestamp of last successful measurement.
uint64_t fail_count() noexcept
Return number of failed measurement attempts.
int64_t last_update_realtime_us() noexcept
Return realtime timestamp of last measurement attempt.
int64_t utc_time_us() noexcept
Return current UTC time in microseconds based on offset.
ts_ms_t now() noexcept
Get the current UTC timestamp in milliseconds.
int64_t now_realtime_us()
Get current real time in microseconds using a platform-specific method.
Main namespace for the Time Shield library.
static void handle_process_exit() noexcept
static NtpTimeServiceT< RunnerT > & instance() noexcept
static uint32_t atexit_registration_count() noexcept
static void begin_process_shutdown() noexcept
static bool is_process_shutting_down() noexcept
Header file with time-related utility functions.