- Connect gate: after the node starts answering, wallet scans wait until the lock state is known and the node knows its tip (or the wallet has fewer than 500 transactions), at most 20 s. A node already behind latches the sync profile from the connect's getinfo. Meanwhile a list-only pass keeps the address list and the cached history up, and the session's first balance and per-address balances are read once even while catching up, so the wallet shows its last-synced values instead of 0. - Background: minimized, hidden, or covered without focus (3 s debounce, any input cancels) polls only the chain state every 15 s. Wallet scans, the mining poll, price, nudges and auto-lock interaction tracking pause; auto-lock now fires while minimized. Leaving a held profile marks every timer due again. - Tab entry marks the timers due instead of forcing scans; a stale list or a never-loaded history still loads on entry. - The scan entry points own the hard gates, failed enumerations keep the previous lists behind a retry hold, and failed balance calls never overwrite known balances with 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
194 lines
6.1 KiB
C++
194 lines
6.1 KiB
C++
#include "refresh_scheduler.h"
|
|
|
|
#include <algorithm>
|
|
|
|
namespace dragonx {
|
|
namespace services {
|
|
|
|
RefreshScheduler::Intervals RefreshScheduler::intervalsForPage(ui::NavPage page)
|
|
{
|
|
using NP = ui::NavPage;
|
|
// Intervals are {core, transactions, addresses, peers} in seconds (0 = disabled).
|
|
// The peers value keeps the status-bar peer count current on EVERY tab — previously it was 0
|
|
// off the Peers tab, so the count never updated until you opened Peers. A slow 20s cadence is
|
|
// plenty for a status-bar number; the Peers tab itself stays fast (5s) for its live list. During
|
|
// sync this is overridden by kSyncProfile (peers 0) so it can't contend with block download.
|
|
switch (page) {
|
|
case NP::Overview: return {2.0f, 10.0f, 15.0f, 20.0f};
|
|
case NP::Send: return {3.0f, 10.0f, 5.0f, 20.0f};
|
|
case NP::Receive: return {5.0f, 15.0f, 5.0f, 20.0f};
|
|
case NP::History: return {5.0f, 3.0f, 15.0f, 20.0f};
|
|
case NP::Mining: return {5.0f, 15.0f, 15.0f, 20.0f};
|
|
case NP::Peers: return {5.0f, 15.0f, 15.0f, 5.0f};
|
|
case NP::Market: return {5.0f, 15.0f, 15.0f, 20.0f};
|
|
case NP::Console: return {10.0f, 30.0f, 30.0f, 30.0f};
|
|
default: return {5.0f, 15.0f, 15.0f, 20.0f};
|
|
}
|
|
}
|
|
|
|
RefreshScheduler::Intervals RefreshScheduler::selectIntervals(ui::NavPage page, bool syncing, bool background)
|
|
{
|
|
if (background) return kBackgroundProfile;
|
|
if (syncing) return kSyncProfile;
|
|
return intervalsForPage(page);
|
|
}
|
|
|
|
static_assert(RefreshScheduler::kSyncProfile.core * 2.0f <= RefreshScheduler::kSyncSettleSeconds,
|
|
"the sync settle window must outlast two Core polls");
|
|
static_assert(RefreshScheduler::kBackgroundProfile.core * 2.0f <= RefreshScheduler::kSyncSettleSeconds,
|
|
"the sync settle window must outlast two Core polls");
|
|
|
|
void RefreshScheduler::applyPage(ui::NavPage page)
|
|
{
|
|
setIntervals(intervalsForPage(page));
|
|
}
|
|
|
|
void RefreshScheduler::setIntervals(Intervals intervals)
|
|
{
|
|
intervals_ = intervals;
|
|
}
|
|
|
|
void RefreshScheduler::tick(float deltaSeconds)
|
|
{
|
|
float delta = std::max(0.0f, deltaSeconds);
|
|
timers_.core += delta;
|
|
timers_.transactions += delta;
|
|
timers_.addresses += delta;
|
|
timers_.peers += delta;
|
|
timers_.price += delta;
|
|
timers_.fast += delta;
|
|
timers_.txAge += delta;
|
|
timers_.opid += delta;
|
|
for (float& h : holds_) {
|
|
if (h > 0.0f) h = std::max(0.0f, h - delta);
|
|
}
|
|
}
|
|
|
|
bool RefreshScheduler::isDue(Timer timer) const
|
|
{
|
|
const float h = hold(timer);
|
|
if (h > 0.0f) return false; // held by deferFor()
|
|
if (h == 0.0f) return true; // the hold ran out: due now, whatever the interval
|
|
float timerInterval = interval(timer);
|
|
return timerInterval > 0.0f && timerRef(timer) >= timerInterval;
|
|
}
|
|
|
|
bool RefreshScheduler::consumeDue(Timer timer)
|
|
{
|
|
if (!isDue(timer)) return false;
|
|
reset(timer);
|
|
return true;
|
|
}
|
|
|
|
void RefreshScheduler::reset(Timer timer)
|
|
{
|
|
timerRef(timer) = 0.0f;
|
|
holdRef(timer) = -1.0f;
|
|
}
|
|
|
|
void RefreshScheduler::markDue(Timer timer)
|
|
{
|
|
holdRef(timer) = -1.0f; // an explicit "now" (user action) overrides a backoff
|
|
float timerInterval = interval(timer);
|
|
timerRef(timer) = timerInterval > 0.0f ? timerInterval : 0.0f;
|
|
}
|
|
|
|
void RefreshScheduler::setTimer(Timer timer, float seconds)
|
|
{
|
|
holdRef(timer) = -1.0f;
|
|
timerRef(timer) = std::max(0.0f, seconds);
|
|
}
|
|
|
|
void RefreshScheduler::deferFor(Timer timer, float seconds)
|
|
{
|
|
holdRef(timer) = std::max(0.0f, seconds);
|
|
}
|
|
|
|
void RefreshScheduler::retrySoon(Timer timer, float seconds)
|
|
{
|
|
holdRef(timer) = -1.0f;
|
|
timerRef(timer) = std::max(0.0f, interval(timer) - std::max(0.0f, seconds));
|
|
}
|
|
|
|
float RefreshScheduler::timer(Timer timer) const
|
|
{
|
|
return timerRef(timer);
|
|
}
|
|
|
|
float RefreshScheduler::interval(Timer timer) const
|
|
{
|
|
switch (timer) {
|
|
case Timer::Core: return intervals_.core;
|
|
case Timer::Transactions: return intervals_.transactions;
|
|
case Timer::Addresses: return intervals_.addresses;
|
|
case Timer::Peers: return intervals_.peers;
|
|
case Timer::Price: return kPrice;
|
|
case Timer::Fast: return kFast;
|
|
case Timer::TxAge: return kTxMaxAge;
|
|
case Timer::Opid: return kOpidPoll;
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
void RefreshScheduler::markImmediateRefresh()
|
|
{
|
|
markDue(Timer::Core);
|
|
markDue(Timer::Transactions);
|
|
markDue(Timer::Addresses);
|
|
markDue(Timer::Peers);
|
|
}
|
|
|
|
void RefreshScheduler::markWalletMutationRefresh()
|
|
{
|
|
markDue(Timer::Core);
|
|
markDue(Timer::Transactions);
|
|
markDue(Timer::Addresses);
|
|
}
|
|
|
|
void RefreshScheduler::resetTxAge()
|
|
{
|
|
reset(Timer::TxAge);
|
|
}
|
|
|
|
bool RefreshScheduler::shouldRefreshTransactions(int lastTxBlockHeight,
|
|
int currentBlockHeight,
|
|
bool transactionsDirty) const
|
|
{
|
|
return lastTxBlockHeight < 0
|
|
|| currentBlockHeight != lastTxBlockHeight
|
|
|| transactionsDirty;
|
|
}
|
|
|
|
float& RefreshScheduler::timerRef(Timer timer)
|
|
{
|
|
switch (timer) {
|
|
case Timer::Core: return timers_.core;
|
|
case Timer::Transactions: return timers_.transactions;
|
|
case Timer::Addresses: return timers_.addresses;
|
|
case Timer::Peers: return timers_.peers;
|
|
case Timer::Price: return timers_.price;
|
|
case Timer::Fast: return timers_.fast;
|
|
case Timer::TxAge: return timers_.txAge;
|
|
case Timer::Opid: return timers_.opid;
|
|
}
|
|
return timers_.core;
|
|
}
|
|
|
|
const float& RefreshScheduler::timerRef(Timer timer) const
|
|
{
|
|
switch (timer) {
|
|
case Timer::Core: return timers_.core;
|
|
case Timer::Transactions: return timers_.transactions;
|
|
case Timer::Addresses: return timers_.addresses;
|
|
case Timer::Peers: return timers_.peers;
|
|
case Timer::Price: return timers_.price;
|
|
case Timer::Fast: return timers_.fast;
|
|
case Timer::TxAge: return timers_.txAge;
|
|
case Timer::Opid: return timers_.opid;
|
|
}
|
|
return timers_.core;
|
|
}
|
|
|
|
} // namespace services
|
|
} // namespace dragonx
|