perf(mining): one save, at most one restart per thread-count gesture
Every thread-count change on the Mining tab saved settings.json and, with a miner running, stopped and restarted it: xmrig re-inits its RandomX dataset and reconnects to the pool, seconds of lost hashing each time. A drag across the grid did this once per tile crossed (up to ~24), with a "Starting pool miner" toast each; every -/+ click or Enter did it again. The tab now hands each pick to the App, which puts the newest in settings at once and applies it when the input settles: on drag release, or 0.5 s after the last click, even if the Mining tab was left in the meantime. The header, RAM estimate and tile fill still follow the drag live. - Pool: the restart is decided on the mining-control thread, after every earlier start or stop has run. It never starts a miner the user stopped, is a no-op when xmrig already runs at that count, and is no longer silently skipped when the pick lands in another restart's stop window (which left xmrig on the old count). - Solo: one setgenerate per gesture. A pick made while one is in flight now waits for it instead of being dropped (another stale count). - Clicking the tile that is already selected does nothing; it used to save settings and restart the miner. - With idle thread scaling on, the grid no longer snaps back to the old count while a pick is on its way to the miner. A pool auto-balance restart keeps a count still queued. - Idle thread scaling changes the pool count through the same retarget. Its correction after a start compares with the count last requested, not the xmrig API's lagging report, so it comes about 1 s in instead of after the first API report (3-5 s). - A retarget whose pool config no longer validates shows the same error and stops the miner, as the old stop-then-start did. - While a benchmark runs a pick is saved, but the miner change is dropped: the benchmark owns the count until it ends. A crash within 0.5 s of a pick, or mid-drag, loses that pick; every clean exit saves it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include "services/scan_policy.h"
|
||||
#include "services/connect_scan_gate.h"
|
||||
#include "services/chat_scan_policy.h"
|
||||
#include "services/mining_thread_commit.h"
|
||||
#include "services/network_refresh_service.h"
|
||||
#include "services/refresh_scheduler.h"
|
||||
#include "services/note_consolidation.h"
|
||||
@@ -5686,6 +5687,68 @@ void testConsoleInputModel()
|
||||
EXPECT_EQ(errorLines[0].text, std::string("Error: bad rpc"));
|
||||
}
|
||||
|
||||
void testMiningThreadCommit()
|
||||
{
|
||||
using dragonx::services::MiningThreadCommit;
|
||||
using dragonx::services::effectivePoolThreads;
|
||||
int out = 0;
|
||||
|
||||
// Stepper: three clicks inside the debounce window -> one commit, with the last value.
|
||||
{
|
||||
MiningThreadCommit c;
|
||||
c.select(5, 10.0); c.select(6, 10.1); c.select(7, 10.2);
|
||||
EXPECT_TRUE(c.pending());
|
||||
EXPECT_FALSE(c.take(10.5, false, out)); // 0.3 s after the last click: not yet
|
||||
EXPECT_TRUE(c.take(10.75, false, out));
|
||||
EXPECT_EQ(out, 7);
|
||||
EXPECT_FALSE(c.pending());
|
||||
EXPECT_FALSE(c.take(11.0, false, out)); // consumed exactly once
|
||||
}
|
||||
// Drag: nothing commits while held, however long; the release commits the last tile at once.
|
||||
{
|
||||
MiningThreadCommit c;
|
||||
for (int i = 1; i <= 16; ++i) {
|
||||
c.select(i, 20.0 + i * 0.1);
|
||||
EXPECT_FALSE(c.take(20.0 + i * 0.1 + 0.9, true, out));
|
||||
}
|
||||
EXPECT_FALSE(c.take(40.0, true, out)); // still held 20 s later
|
||||
c.release(40.1);
|
||||
EXPECT_TRUE(c.take(40.1, false, out));
|
||||
EXPECT_EQ(out, 16);
|
||||
}
|
||||
// Plain click in real frame order: the tab's render selects (button down); on the next frame the App's
|
||||
// tick sees the button up, calls release() and then take(): it commits on that frame, not 0.5 s later.
|
||||
{
|
||||
MiningThreadCommit c;
|
||||
c.select(4, 30.0);
|
||||
c.release(30.016);
|
||||
EXPECT_TRUE(c.take(30.016, false, out));
|
||||
EXPECT_EQ(out, 4);
|
||||
}
|
||||
// release() with nothing pending changes nothing.
|
||||
{
|
||||
MiningThreadCommit c;
|
||||
c.release(1.0);
|
||||
EXPECT_FALSE(c.pending());
|
||||
EXPECT_FALSE(c.take(2.0, false, out));
|
||||
}
|
||||
// A new pick during the wait pushes the deadline out; cancel() drops it.
|
||||
{
|
||||
MiningThreadCommit c;
|
||||
c.select(2, 50.0);
|
||||
c.select(9, 50.4);
|
||||
EXPECT_FALSE(c.take(50.6, false, out));
|
||||
c.cancel();
|
||||
EXPECT_FALSE(c.pending());
|
||||
EXPECT_FALSE(c.take(60.0, false, out));
|
||||
}
|
||||
// Which pool count idle scaling compares with.
|
||||
EXPECT_EQ(effectivePoolThreads(8, 4, 2), 8); // a queued restart's target beats the stale values
|
||||
EXPECT_EQ(effectivePoolThreads(0, 4, 2), 4);
|
||||
EXPECT_EQ(effectivePoolThreads(0, 0, 2), 2);
|
||||
EXPECT_EQ(effectivePoolThreads(0, 0, 0), 0);
|
||||
}
|
||||
|
||||
void testMiningBenchmarkModel()
|
||||
{
|
||||
dragonx::ui::ThreadBenchmark benchmark;
|
||||
@@ -12660,6 +12723,7 @@ int main()
|
||||
testRendererHelpers();
|
||||
testConsoleInputModel();
|
||||
testMiningBenchmarkModel();
|
||||
testMiningThreadCommit();
|
||||
testBalanceAddressListModel();
|
||||
testExplorerBlockCache();
|
||||
testTransactionHistoryCache();
|
||||
|
||||
Reference in New Issue
Block a user