Wait for an initial offset prior to proceeding with initialization

This commit is contained in:
Dan Paulat 2025-08-22 22:28:55 -05:00
parent 88d968a533
commit c76c9b57ed
3 changed files with 49 additions and 3 deletions

View file

@ -17,6 +17,7 @@ void Initialize()
ntpClient_ = network::NtpClient::Instance(); ntpClient_ = network::NtpClient::Instance();
ntpClient_->Start(); ntpClient_->Start();
ntpClient_->WaitForInitialOffset();
} }
void Shutdown() void Shutdown()

View file

@ -35,6 +35,8 @@ public:
std::string RotateServer(); std::string RotateServer();
void RunOnce(); void RunOnce();
void WaitForInitialOffset();
static std::shared_ptr<NtpClient> Instance(); static std::shared_ptr<NtpClient> Instance();
private: private:

View file

@ -3,6 +3,9 @@
#include <scwx/util/logger.hpp> #include <scwx/util/logger.hpp>
#include <scwx/util/threads.hpp> #include <scwx/util/threads.hpp>
#include <condition_variable>
#include <mutex>
#include <boost/asio/ip/udp.hpp> #include <boost/asio/ip/udp.hpp>
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
#include <boost/asio/thread_pool.hpp> #include <boost/asio/thread_pool.hpp>
@ -112,6 +115,8 @@ public:
void Run(); void Run();
void RunOnce(); void RunOnce();
void FinishInitialization();
boost::asio::thread_pool threadPool_ {2u}; boost::asio::thread_pool threadPool_ {2u};
boost::asio::steady_timer pollTimer_ {threadPool_}; boost::asio::steady_timer pollTimer_ {threadPool_};
@ -122,6 +127,10 @@ public:
bool disableServer_ {false}; bool disableServer_ {false};
bool rotateServer_ {false}; bool rotateServer_ {false};
std::mutex initializationMutex_ {};
std::condition_variable initializationCondition_ {};
std::atomic<bool> initialized_ {false};
types::ntp::NtpPacket transmitPacket_ {}; types::ntp::NtpPacket transmitPacket_ {};
boost::asio::ip::udp::socket socket_ {threadPool_}; boost::asio::ip::udp::socket socket_ {threadPool_};
@ -164,6 +173,14 @@ NtpClient::Impl::Impl()
transmitPacket_.fields.vn = 3; // Version transmitPacket_.fields.vn = 3; // Version
transmitPacket_.fields.mode = 3; // Client (3) transmitPacket_.fields.mode = 3; // Client (3)
// If the NTP client is enabled, wait until the first refresh to consider
// "initialized". Otherwise, mark as initialized immediately to prevent a
// deadlock.
if (!enabled_)
{
initialized_ = true;
}
} }
NtpClient::Impl::~Impl() NtpClient::Impl::~Impl()
@ -253,7 +270,7 @@ void NtpClient::Impl::Poll()
{ {
using namespace std::chrono_literals; using namespace std::chrono_literals;
static constexpr auto kTimeout_ = 15s; static constexpr auto kTimeout_ = 5s;
try try
{ {
@ -359,8 +376,6 @@ void NtpClient::Impl::ReceivePacket(std::size_t length)
timeOffset_ = ((t1 - t0) + (t2 - t3)) / 2; timeOffset_ = ((t1 - t0) + (t2 - t3)) / 2;
logger_->debug("Time offset updated: {:%jd %T}", timeOffset_); logger_->debug("Time offset updated: {:%jd %T}", timeOffset_);
// TODO: Signal
} }
} }
else else
@ -496,6 +511,34 @@ void NtpClient::Impl::RunOnce()
// Did not poll this frame // Did not poll this frame
error_ = true; error_ = true;
} }
FinishInitialization();
}
void NtpClient::Impl::FinishInitialization()
{
if (!initialized_)
{
// Set initialized to true
std::unique_lock lock(initializationMutex_);
initialized_ = true;
lock.unlock();
// Notify any threads waiting for initialization
initializationCondition_.notify_all();
}
}
void NtpClient::WaitForInitialOffset()
{
std::unique_lock lock(p->initializationMutex_);
// While not yet initialized
while (!p->initialized_)
{
// Wait for initialization
p->initializationCondition_.wait(lock);
}
} }
std::shared_ptr<NtpClient> NtpClient::Instance() std::shared_ptr<NtpClient> NtpClient::Instance()