Time Shield Library 2.0.0
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_HEADER_NTP_NTP_CLIENT_POOL_RUNNER_HPP_INCLUDED
4#define TIME_SHIELD_HEADER_NTP_NTP_CLIENT_POOL_RUNNER_HPP_INCLUDED
5
7
8#if TIME_SHIELD_ENABLE_NTP_CLIENT
9
10#include "ntp_client_pool.hpp"
11
12#include <atomic>
13#include <chrono>
14#include <condition_variable>
15#include <cstdint>
16#include <mutex>
17#include <thread>
18#include <utility>
19#include <vector>
20
21namespace time_shield {
22
38 template <class PoolT>
40 public:
43 explicit BasicPoolRunner(PoolT pool = PoolT{})
44 : m_pool(std::move(pool)) {}
45
48 stop();
49 }
50
55 bool start(std::chrono::milliseconds interval = std::chrono::seconds(30),
56 bool measure_immediately = true) {
57 if (m_is_running.load()) {
58 return false;
59 }
60 if (interval.count() <= 0) {
61 interval = std::chrono::milliseconds(1);
62 }
63
64 m_is_stop_requested.store(false);
65 m_is_force_requested.store(false);
66 m_is_running.store(true);
67
68 try {
69 m_thread = std::thread(&BasicPoolRunner::run_loop, this, interval, measure_immediately);
70 } catch (...) {
71 m_is_running.store(false);
72 return false;
73 }
74
75 return true;
76 }
77
82 bool start(int interval_ms, bool measure_immediately = true) {
83 return start(std::chrono::milliseconds(interval_ms), measure_immediately);
84 }
85
87 void stop() {
88 m_is_stop_requested.store(true);
89 m_cv.notify_all();
90 if (m_thread.joinable()) {
91 m_thread.join();
92 }
93 m_is_running.store(false);
94 }
95
98 bool running() const noexcept { return m_is_running.load(); }
99
103 if (!m_is_running.load()) {
104 return false;
105 }
106 m_is_force_requested.store(true);
107 m_cv.notify_one();
108 return true;
109 }
110
113 bool measure_now() {
114 return do_measure();
115 }
116
119 int64_t offset_us() const noexcept { return m_pool.offset_us(); }
122 int64_t utc_time_us() const noexcept { return m_pool.utc_time_us(); }
125 int64_t utc_time_ms() const noexcept { return m_pool.utc_time_ms(); }
128 int64_t utc_time_sec() const noexcept { return utc_time_us() / 1000000; }
129
132 bool last_measure_ok() const noexcept { return m_last_measure_ok.load(); }
135 uint64_t measure_count() const noexcept { return m_measure_count.load(); }
138 uint64_t fail_count() const noexcept { return m_fail_count.load(); }
141 int64_t last_update_realtime_us() const noexcept { return m_last_update_realtime_us.load(); }
144 int64_t last_success_realtime_us() const noexcept { return m_last_success_realtime_us.load(); }
145
148 std::vector<NtpSample> last_samples() const { return m_pool.last_samples(); }
149
150 private:
151 void run_loop(std::chrono::milliseconds interval, bool measure_immediately) {
152 bool is_first = measure_immediately;
153 while (!m_is_stop_requested.load()) {
154 if (is_first) {
155 do_measure();
156 is_first = false;
157 } else {
158 std::unique_lock<std::mutex> lk(m_cv_mtx);
159 m_cv.wait_for(lk, interval, [this]() {
160 return m_is_stop_requested.load() || m_is_force_requested.load();
161 });
162 if (m_is_stop_requested.load()) {
163 break;
164 }
165 m_is_force_requested.store(false);
166 do_measure();
167 }
168 }
169 m_is_running.store(false);
170 }
171
172 bool do_measure() {
173 bool is_ok = false;
174 try {
175 std::lock_guard<std::mutex> lk(m_pool_mtx);
176 is_ok = m_pool.measure();
177 } catch (...) {
178 is_ok = false;
179 }
180
181 m_measure_count.fetch_add(1);
182 if (!is_ok) {
183 m_fail_count.fetch_add(1);
184 }
185
186 m_last_measure_ok.store(is_ok);
187
188 const int64_t now = now_realtime_us();
190 if (is_ok) {
192 }
193
194 return is_ok;
195 }
196
197 private:
198 PoolT m_pool;
199 mutable std::mutex m_pool_mtx;
200
201 std::thread m_thread;
202 std::condition_variable m_cv;
203 std::mutex m_cv_mtx;
204
205 std::atomic<bool> m_is_running{false};
206 std::atomic<bool> m_is_stop_requested{false};
207 std::atomic<bool> m_is_force_requested{false};
208
209 std::atomic<bool> m_last_measure_ok{false};
210 std::atomic<uint64_t> m_measure_count{0};
211 std::atomic<uint64_t> m_fail_count{0};
212 std::atomic<int64_t> m_last_update_realtime_us{0};
213 std::atomic<int64_t> m_last_success_realtime_us{0};
214 };
215
217
218} // namespace time_shield
219
220#else // TIME_SHIELD_ENABLE_NTP_CLIENT
221
222namespace time_shield {
224 public:
226 static_assert(sizeof(void*) == 0, "NtpClientPoolRunner is disabled by configuration.");
227 }
228 };
229} // namespace time_shield
230
231#endif // _TIME_SHIELD_ENABLE_NTP_CLIENT
232
233#endif // TIME_SHIELD_HEADER_NTP_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.
std::atomic< int64_t > m_last_update_realtime_us
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.
std::atomic< uint64_t > m_measure_count
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.
std::atomic< int64_t > m_last_success_realtime_us
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.
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.