3#ifndef TIME_SHIELD_HEADER_NTP_NTP_TIME_SERVICE_HPP_INCLUDED
4#define TIME_SHIELD_HEADER_NTP_NTP_TIME_SERVICE_HPP_INCLUDED
8#if TIME_SHIELD_ENABLE_NTP_CLIENT
15#include <condition_variable>
26 template <
class RunnerT>
27 class NtpTimeServiceT;
30 template <
class RunnerT>
31 struct NtpTimeServiceSingleton;
33 template <
class RunnerT>
34 struct NtpTimeServiceTestAccess;
36#ifdef TIME_SHIELD_TEST_FAKE_NTP
54 bool start(std::chrono::milliseconds interval = std::chrono::seconds(30),
55 bool measure_immediately =
true) {
59 if (interval.count() <= 0) {
60 interval = std::chrono::milliseconds(1);
77 bool start(
int interval_ms,
bool measure_immediately =
true) {
78 return start(std::chrono::milliseconds(interval_ms), measure_immediately);
142 std::unique_lock<std::mutex> lk(
m_cv_mtx);
143 m_cv.wait_for(lk, sleep_interval, [
this]() {
159 m_offset_us.store(
static_cast<int64_t
>(count * 1000));
168 std::chrono::milliseconds
m_interval{std::chrono::seconds(30)};
197 template <
class RunnerT>
222 bool init(std::chrono::milliseconds interval,
bool measure_immediately =
true) {
227 std::unique_ptr<RunnerT> local_runner;
228 std::chrono::milliseconds start_interval = interval;
229 bool use_measure_immediately = measure_immediately;
231 std::unique_lock<std::mutex> lk(
m_mtx);
241 if (start_interval.count() <= 0) {
242 start_interval = std::chrono::milliseconds(1);
257 bool has_started =
false;
260 has_started = local_runner->start(start_interval, use_measure_immediately);
262 is_ok = local_runner->measure_now();
271 local_runner->stop();
276 local_runner.reset();
280 std::lock_guard<std::mutex> lk(
m_mtx);
282 m_last_offset_us.store(local_runner->offset_us(), std::memory_order_relaxed);
298 bool init(
int interval_ms,
bool measure_immediately =
true) {
299 return init(std::chrono::milliseconds(interval_ms), measure_immediately);
304 std::unique_ptr<RunnerT> local_runner;
306 std::unique_lock<std::mutex> lk(
m_mtx);
317 local_runner->stop();
322 std::lock_guard<std::mutex> lk(
m_mtx);
331 std::lock_guard<std::mutex> lk(
m_mtx);
355 std::lock_guard<std::mutex> lk(
m_mtx);
357 const int64_t offset =
m_runner->offset_us();
371 std::lock_guard<std::mutex> lk(
m_mtx);
392 std::lock_guard<std::mutex> lk(
m_mtx);
400 std::lock_guard<std::mutex> lk(
m_mtx);
408 std::lock_guard<std::mutex> lk(
m_mtx);
416 std::lock_guard<std::mutex> lk(
m_mtx);
418 return m_runner->last_update_realtime_us();
424 std::lock_guard<std::mutex> lk(
m_mtx);
426 return m_runner->last_success_realtime_us();
432 bool stale(std::chrono::milliseconds max_age)
const noexcept {
438 return age >
static_cast<int64_t
>(max_age.count()) * 1000;
444 bool stale(
int max_age_ms)
const noexcept {
445 return stale(std::chrono::milliseconds(max_age_ms));
452 std::lock_guard<std::mutex> lk(
m_mtx);
464 std::lock_guard<std::mutex> lk(
m_mtx);
476 std::lock_guard<std::mutex> lk(
m_mtx);
489 std::lock_guard<std::mutex> lk(
m_mtx);
501 std::lock_guard<std::mutex> lk(
m_mtx);
511 std::lock_guard<std::mutex> lk(
m_mtx);
528 std::unique_ptr<RunnerT> new_runner;
529 std::unique_ptr<RunnerT> old_runner;
530 std::chrono::milliseconds interval;
531 bool measure_immediately =
true;
532 bool was_running =
false;
534 std::unique_lock<std::mutex> lk(
m_mtx);
562 bool has_started =
false;
565 has_started = new_runner->start(interval, measure_immediately);
567 is_ok = new_runner->measure_now();
585 std::lock_guard<std::mutex> lk(
m_mtx);
625 std::unique_ptr<RunnerT> local_runner;
627 std::unique_lock<std::mutex> lk(
m_mtx);
642 local_runner->stop();
649 std::lock_guard<std::mutex> lk(
m_mtx);
687 std::vector<NtpServerConfig> servers;
696 pool.set_servers(std::move(servers));
698 std::unique_ptr<RunnerT> runner;
700 runner.reset(
new RunnerT(std::move(pool)));
714 std::chrono::milliseconds
m_interval{std::chrono::seconds(30)};
727 template <
class RunnerT>
739 instance().begin_process_shutdown();
743 template <
class RunnerT>
759#if defined(TIME_SHIELD_TEST_FAKE_NTP)
776 inline bool init(std::chrono::milliseconds interval = std::chrono::seconds(30),
777 bool measure_immediately =
true) {
786 inline bool init(
int interval_ms,
787 bool measure_immediately =
true) {
864 inline bool stale(std::chrono::milliseconds max_age)
noexcept {
872 inline bool stale(
int max_age_ms)
noexcept {
886 static_assert(
sizeof(
void*) == 0,
"NtpTimeService is disabled by configuration.");
static std::vector< NtpServerConfig > build_default_servers()
Build a conservative default server list.
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< RunnerT > 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()
Fake runner for tests without network access.
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
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