6#include <core/net/EventLoop.hpp>
11#include "../attributes.hpp"
15namespace morph::offline {
83 Callback onOffline MORPH_LIFETIMEBOUND,
Callback onOnline MORPH_LIFETIMEBOUND,
86 _state{std::make_shared<State>(loop, std::move(probe), std::move(onOffline), std::move(onOnline), cfg)} {
99 : _ownedLoop{std::make_unique<::morph::exec::IoLoop>()},
100 _loop{_ownedLoop.get()},
102 std::make_shared<State>(*_ownedLoop, std::move(probe), std::move(onOffline), std::move(onOnline), cfg)} {
125 [[nodiscard]]
bool isOnline() const noexcept {
return _state->online.load(); }
134 _loop->runAndWait([state = _state] { state->stop(); });
139 struct State : std::enable_shared_from_this<State> {
142 probe{std::move(probeFn)},
143 onOffline{std::move(offline)},
144 onOnline{std::move(online_)},
152 ::morph::exec::detail::noteOwner(
"NetworkMonitor::stop", loop.
loop(), loop.
runningHere());
157 static_cast<void>(loop.loop().cancelTimer(timer));
160 static bool safeProbe(
const ProbeFunction& probeFn)
noexcept {
162 return probeFn ? probeFn() : false;
168 void handleProbeResult(
bool probeOk) {
170 consecutiveSuccesses = 0;
171 ++consecutiveFailures;
172 if (online.load() && consecutiveFailures >= cfg.failureThreshold) {
179 consecutiveFailures = 0;
180 ++consecutiveSuccesses;
181 if (!online.load() && consecutiveSuccesses >= cfg.onlineThreshold) {
193 static void fire(
void* statePtr) {
196 std::shared_ptr<State>
const keep =
static_cast<State*
>(statePtr)->shared_from_this();
198 ::morph::exec::detail::noteOwner(
"NetworkMonitor::probe",
self.loop.loop(),
self.loop.runningHere());
203 bool const probeOk = safeProbe(
self.probe);
207 self.handleProbeResult(probeOk);
213 ::morph::exec::IoLoop& loop;
218 std::atomic<bool> online{
true};
219 int consecutiveFailures{0};
220 int consecutiveSuccesses{0};
221 ::core::net::TimerId timer{};
226 _loop->post([state = _state] {
227 if (!state->stopped) {
235 std::unique_ptr<::morph::exec::IoLoop> _ownedLoop;
236 ::morph::exec::IoLoop* _loop;
237 std::shared_ptr<State> _state;
constexpr Self self
The dialog or drawer a command list runs in.
Owns a core::net::PlatformLoop and, for IoLoopDriver::OwnThread natively, the one thread that runs it...
::core::net::EventLoop & loop() noexcept
The loop itself, for a component arming timers or opening sockets from inside one of its tasks,...
bool runningHere() const noexcept
Whether the calling thread may touch the loop's state directly.
Connectivity monitor that calls a probe on a timer and fires callbacks on state changes.
NetworkMonitor(ProbeFunction probe, Callback onOffline, Callback onOnline, Config cfg=Config{})
Starts the monitor on a loop of its own, for a caller with no IoLoop to share. Natively that loop own...
std::function< void()> Callback
Called on connectivity state change.
bool isOnline() const noexcept
Returns true if the monitor currently considers the network reachable.
std::function< bool()> ProbeFunction
Returns true when the network is reachable.
NetworkMonitorConfig Config
Alias for the configuration struct.
NetworkMonitor(::morph::exec::IoLoop &loop, ProbeFunction probe, Callback onOffline, Callback onOnline, Config cfg=Config{})
Starts the monitor on loop.
void stop()
Stops probing. Idempotent.
~NetworkMonitor()
Stops the monitor. Once this returns, no probe or callback of this monitor will run again,...
The check every loop-owned body makes on entry: "am I on my owner?".
Configuration for NetworkMonitor.
int onlineThreshold
Consecutive successes required before going online.
std::chrono::milliseconds probeInterval
Time between probe calls.
int failureThreshold
Consecutive failures required before going offline.