feat(lite): M2b-3 — background refresh worker + App::update hook
- LiteWalletController owns a background std::thread worker that, once a wallet is ready,
refreshes every ~4s and publishes a copyable LiteWalletAppRefreshModel under a mutex.
Worker auto-starts on lifecycle-ready and is stopped+joined in the destructor. status_
is written only on the main thread; walletOpen_/syncStarted_ are atomic.
- App::update() calls takeRefreshedModel() and applies it into state_ on the main thread
(WalletState is non-copyable, so the model crosses the thread boundary, not the state),
so the existing Balance/Receive/Transactions tabs populate from lite data.
- refreshWalletState() refactored onto refreshModel() (pure, worker-safe).
- testLiteWalletControllerWorkerProducesModel verifies the worker publishes a populated
model (stable across repeated runs). Builds clean in all configs.
Real-backend smoke (lite_smoke --refresh now runs real output through the parsers) found
two integration bugs, documented in the plan for follow-up:
- syncstatus parser requires synced_blocks/total_blocks but the real idle response is
{"syncing":"false"} (string), so it fails to parse when not actively syncing.
- the first data query (balance/list) blocks on a full chain sync, which would hang the
worker's shutdown join — needs a cancel/timeout path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
@@ -4650,6 +4651,38 @@ void testLiteWalletControllerRefreshPopulatesState()
|
||||
EXPECT_FALSE(fresh.refreshWalletState(empty));
|
||||
}
|
||||
|
||||
// M2b-3: opening a wallet auto-starts the background worker, which produces a refresh model
|
||||
// the main thread can pick up via takeRefreshedModel() and apply to WalletState.
|
||||
void testLiteWalletControllerWorkerProducesModel()
|
||||
{
|
||||
using namespace dragonx::wallet;
|
||||
const auto caps = makeWalletCapabilities(WalletBuildKind::Lite, /*embeddedDaemon*/ false, /*liteBackendLinked*/ true);
|
||||
const auto conn = defaultLiteConnectionSettings();
|
||||
|
||||
LiteWalletController controller(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
|
||||
EXPECT_TRUE(controller.createWallet(LiteWalletCreateRequest{}).walletReady); // auto-starts the worker
|
||||
|
||||
// The worker refreshes immediately on start; poll briefly (<=2s) for the produced model.
|
||||
LiteWalletAppRefreshModel model;
|
||||
bool got = false;
|
||||
for (int i = 0; i < 200 && !got; ++i) {
|
||||
got = controller.takeRefreshedModel(model);
|
||||
if (!got) std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
EXPECT_TRUE(got);
|
||||
EXPECT_TRUE(model.hasBalance);
|
||||
EXPECT_TRUE(model.hasAddresses);
|
||||
|
||||
dragonx::WalletState state;
|
||||
applyLiteRefreshModelToWalletState(model, state);
|
||||
EXPECT_NEAR(state.privateBalance, 2.0, 1e-9);
|
||||
|
||||
// Idle controller (no wallet -> no worker) has nothing pending.
|
||||
LiteWalletController idle(caps, conn, LiteClientBridge::fromApi(dragonx::test::makeFakeLiteApi()));
|
||||
LiteWalletAppRefreshModel none;
|
||||
EXPECT_FALSE(idle.takeRefreshedModel(none));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
@@ -4687,6 +4720,7 @@ int main()
|
||||
testLiteChainNameMigration();
|
||||
testLiteRefreshModelAppliesToWalletState();
|
||||
testLiteWalletControllerRefreshPopulatesState();
|
||||
testLiteWalletControllerWorkerProducesModel();
|
||||
testLiteBridgeRuntimeShutdownIsIdempotent();
|
||||
testLiteBridgeRuntimeDestructorCallsShutdownOnce();
|
||||
testLiteBridgeRuntimeShutdownWaitsForOwnedStringRelease();
|
||||
|
||||
Reference in New Issue
Block a user