Time Shield Library
C++ library for working with time
Loading...
Searching...
No Matches
ntp_client_pool_runner.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2#pragma once
3#ifndef _TIME_SHIELD_NTP_CLIENT_POOL_RUNNER_HPP_INCLUDED
4#define _TIME_SHIELD_NTP_CLIENT_POOL_RUNNER_HPP_INCLUDED
5
6#include "config.hpp"
7
8#if TIME_SHIELD_ENABLE_NTP_CLIENT
9
10#include "ntp_client_pool.hpp"
11#include "time_utils.hpp"
12
13#include <atomic>
14#include <chrono>
15#include <condition_variable>
16#include <cstdint>
17#include <mutex>
18#include <thread>
19#include <utility>
20#include <vector>
21
22namespace time_shield {
23
39 template <class PoolT>
41 public:
44 explicit BasicPoolRunner(PoolT pool = PoolT{})
45 : m_pool(std::move(pool)) {}
46
49 stop();
50 }
51
56 bool start(std::chrono::milliseconds interval = std::chrono::seconds(30),
57 bool measure_immediately = true) {
58 if (m_is_running.load()) {
59 return false;
60 }
61 if (interval.count() <= 0) {
62 interval = std::chrono::milliseconds(1);
63 }
64
65 m_is_stop_requested.store(false);
66 m_is_force_requested.store(false);
67 m_is_running.store(true);
68
69 try {
70 m_thread = std::thread(&BasicPoolRunner::run_loop, this, interval, measure_immediately);
71 } catch (...) {
72 m_is_running.store(false);
73 return false;
74 }
75
76 return true;
77 }
78
83 bool start(int interval_ms, bool measure_immediately = true) {
84 return start(std::chrono::milliseconds(interval_ms), measure_immediately);
85 }
86
88 void stop() {
89 m_is_stop_requested.store(true);
90 m_cv.notify_all();
91 if (m_thread.joinable()) {
92 m_thread.join();
93 }
94 m_is_running.store(false);
95 }
96
99 bool running() const noexcept { return m_is_running.load(); }
100
104 if (!m_is_running.load()) {
105 return false;
106 }
107 m_is_force_requested.store(true);
108 m_cv.notify_one();
109 return true;
110 }
111
114 bool measure_now() {
115 return do_measure();
116 }
117
120 int64_t offset_us() const noexcept { return m_pool.offset_us(); }
123 int64_t utc_time_us() const noexcept { return m_pool.utc_time_us(); }
126 int64_t utc_time_ms() const noexcept { return m_pool.utc_time_ms(); }
129 int64_t utc_time_sec() const noexcept { return utc_time_us() / 1000000; }
130
133 bool last_measure_ok() const noexcept { return m_last_measure_ok.load(); }
136 uint64_t measure_count() const noexcept { return m_measure_count.load(); }
139 uint64_t fail_count() const noexcept { return m_fail_count.load(); }
142 int64_t last_update_realtime_us() const noexcept { return m_last_update_realtime_us.load(); }
145 int64_t last_success_realtime_us() const noexcept { return m_last_success_realtime_us.load(); }
146
149 std::vector<NtpSample> last_samples() const { return m_pool.last_samples(); }
150
151 private:
152 void run_loop(std::chrono::milliseconds interval, bool measure_immediately) {
153 bool is_first = measure_immediately;
154 while (!m_is_stop_requested.load()) {
155 if (is_first) {
156 do_measure();
157 is_first = false;
158 } else {
159 std::unique_lock<std::mutex> lk(m_cv_mtx);
160 m_cv.wait_for(lk, interval, [this]() {
161 return m_is_stop_requested.load() || m_is_force_requested.load();
162 });
163 if (m_is_stop_requested.load()) {
164 break;
165 }
166 m_is_force_requested.store(false);
167 do_measure();
168 }
169 }
170 m_is_running.store(false);
171 }
172
173 bool do_measure() {
174 bool is_ok = false;
175 try {
176 std::lock_guard<std::mutex> lk(m_pool_mtx);
177 is_ok = m_pool.measure();
178 } catch (...) {
179 is_ok = false;
180 }
181
182 m_measure_count.fetch_add(1);
183 if (!is_ok) {
184 m_fail_count.fetch_add(1);
185 }
186
187 m_last_measure_ok.store(is_ok);
188
189 const int64_t now = now_realtime_us();
191 if (is_ok) {
193 }
194
195 return is_ok;
196 }
197
198 private:
199 PoolT m_pool;
200 mutable std::mutex m_pool_mtx;
201
202 std::thread m_thread;
203 std::condition_variable m_cv;
204 std::mutex m_cv_mtx;
205
206 std::atomic<bool> m_is_running{false};
207 std::atomic<bool> m_is_stop_requested{false};
208 std::atomic<bool> m_is_force_requested{false};
209
210 std::atomic<bool> m_last_measure_ok{false};
211 std::atomic<uint64_t> m_measure_count{0};
212 std::atomic<uint64_t> m_fail_count{0};
213 std::atomic<int64_t> m_last_update_realtime_us{0};
214 std::atomic<int64_t> m_last_success_realtime_us{0};
215 };
216
218
219} // namespace time_shield
220
221#else // TIME_SHIELD_ENABLE_NTP_CLIENT
222
223namespace time_shield {
225 public:
227 static_assert(sizeof(void*) == 0, "NtpClientPoolRunner is disabled by configuration.");
228 }
229 };
230} // namespace time_shield
231
232#endif // _TIME_SHIELD_ENABLE_NTP_CLIENT
233
234#endif // _TIME_SHIELD_NTP_CLIENT_POOL_RUNNER_HPP_INCLUDED
Background runner that periodically measures NTP offsets using a pool.
bool start(int interval_ms, bool measure_immediately=true)
Start periodic measurements using milliseconds.
uint64_t fail_count() const noexcept
Return number of failed measurement attempts.
~BasicPoolRunner()
Stop background thread on destruction.
bool start(std::chrono::milliseconds interval=std::chrono::seconds(30), bool measure_immediately=true)
Start periodic measurements on a background thread.
uint64_t measure_count() const noexcept
Return total number of measurement attempts.
int64_t utc_time_us() const noexcept
Return current UTC time in microseconds using pool offset.
int64_t utc_time_sec() const noexcept
Return current UTC time in seconds using pool offset.
bool force_measure()
Wake the worker thread and request a measurement.
void run_loop(std::chrono::milliseconds interval, bool measure_immediately)
bool measure_now()
Perform one measurement immediately.
bool last_measure_ok() const noexcept
Return whether last measurement updated the offset.
bool running() const noexcept
Return true when background thread is running.
int64_t offset_us() const noexcept
Return last estimated offset in microseconds.
BasicPoolRunner(PoolT pool=PoolT{})
Construct runner with a pool instance.
int64_t utc_time_ms() const noexcept
Return current UTC time in milliseconds using pool offset.
int64_t last_update_realtime_us() const noexcept
Return realtime timestamp of last measurement attempt.
void stop()
Stop background measurements.
std::vector< NtpSample > last_samples() const
Return copy of the most recent samples.
int64_t last_success_realtime_us() const noexcept
Return realtime timestamp of last successful measurement.
Configuration macros for the library.
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.
Header file with time-related utility functions.