Improve startup performance, shutdown reliability, and Qt5 compatibility

- Defer SecureMsgStart and ReacceptWalletTransactions to background thread
  so GUI appears faster during startup
- Add shutdown checks in wallet scan/reaccept to prevent hanging on exit
- Smart wallet rescan: start from wallet birthday instead of genesis block
- Raise default dbcache from 25 to 128 MB for better performance
- Replace deprecated QDesktopServices with QStandardPaths (Qt5 compat)
- Enforce Qt5+ requirement in build system, remove -fpermissive flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 23:04:29 -07:00
parent 6af3f819ed
commit 0f34c7d26f
7 changed files with 156 additions and 55 deletions
+1 -2
View File
@@ -79,7 +79,7 @@ bool CDBEnv::Open(fs::path pathEnv_)
if (GetBoolArg("-privdb", true))
nEnvFlags |= DB_PRIVATE;
int nDbCache = GetArg("-dbcache", 25);
int nDbCache = GetArg("-dbcache", 128);
dbenv.set_lg_dir(pathLogDir.string().c_str());
dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
dbenv.set_lg_bsize(1048576);
@@ -593,4 +593,3 @@ bool CAddrDB::Read(CAddrMan& addr)
return true;
}
+66 -10
View File
@@ -38,6 +38,9 @@ unsigned int nDerivationMethodIndex;
bool fUseFastIndex;
enum Checkpoints::CPMode CheckpointsMode;
static CCriticalSection cs_DeferredStartup;
static bool fDeferredStartupRunning = false;
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
@@ -69,6 +72,46 @@ bool ShutdownRequested()
return fRequestShutdown;
}
void ThreadDeferredStartup(void* parg)
{
// Make this thread recognisable as the deferred startup worker.
RenameThread("Triangles-postinit");
int64_t nTotalStart = GetTimeMillis();
printf("Starting deferred startup tasks...\n");
try
{
if (!fShutdown)
{
int64_t nStart = GetTimeMillis();
SecureMsgStart(fNoSmsg, GetBoolArg("-smsgscanchain"));
printf(" securemsg %15"PRId64"ms\n", GetTimeMillis() - nStart);
}
if (!fShutdown && pwalletMain)
{
int64_t nStart = GetTimeMillis();
pwalletMain->ReacceptWalletTransactions();
printf(" reaccept %15"PRId64"ms\n", GetTimeMillis() - nStart);
}
printf("Deferred startup tasks finished %"PRId64"ms\n", GetTimeMillis() - nTotalStart);
}
catch (std::exception& e)
{
PrintExceptionContinue(&e, "ThreadDeferredStartup()");
}
catch (...)
{
PrintExceptionContinue(NULL, "ThreadDeferredStartup()");
}
{
LOCK(cs_DeferredStartup);
fDeferredStartupRunning = false;
}
}
void Shutdown(void* parg)
{
static CCriticalSection cs_Shutdown;
@@ -90,7 +133,19 @@ void Shutdown(void* parg)
if (fFirstThread)
{
fShutdown = true;
int64_t nDeferredWaitStart = GetTimeMillis();
while (true)
{
bool fDeferredRunning;
{
LOCK(cs_DeferredStartup);
fDeferredRunning = fDeferredStartupRunning;
}
if (!fDeferredRunning || GetTimeMillis() - nDeferredWaitStart > 5000)
break;
MilliSleep(50);
}
SecureMsgShutdown();
nTransactionsUpdated++;
@@ -921,10 +976,6 @@ bool AppInit2()
addrman.size(), GetTimeMillis() - nStart);
// ********************************************************* Step 10.1: startup secure messaging
SecureMsgStart(fNoSmsg, GetBoolArg("-smsgscanchain"));
// ********************************************************* Step 11: start node
if (!CheckDiskSpace())
@@ -945,6 +996,16 @@ bool AppInit2()
if (fServer)
NewThread(ThreadRPCServer, NULL);
{
LOCK(cs_DeferredStartup);
fDeferredStartupRunning = true;
}
if (!NewThread(ThreadDeferredStartup, NULL))
{
printf("Warning: deferred startup thread could not be started, running inline\n");
ThreadDeferredStartup(NULL);
}
// ********************************************************* Step 12: finished
uiInterface.InitMessage(_("Done loading"));
@@ -953,9 +1014,6 @@ bool AppInit2()
if (!strErrors.str().empty())
return InitError(strErrors.str());
// Add wallet transactions that aren't already in a block to mapTransactions
pwalletMain->ReacceptWalletTransactions();
#if !defined(QT_GUI)
// Loop until process is exit()ed from shutdown() function,
// called from ThreadRPCServer thread when a "stop" command is received.
@@ -965,5 +1023,3 @@ bool AppInit2()
return true;
}
+2 -2
View File
@@ -16,7 +16,7 @@
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>
#include <QDesktopServices>
#include <QStandardPaths>
#include <QThread>
#include <boost/filesystem.hpp>
@@ -172,7 +172,7 @@ QString getSaveFileName(QWidget *parent, const QString &caption,
QString myDir;
if(dir.isEmpty()) // Default to user documents location
{
myDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
myDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
}
else
{
+2 -2
View File
@@ -64,7 +64,7 @@
#include <QDateTime>
#include <QMovie>
#include <QFileDialog>
#include <QDesktopServices>
#include <QStandardPaths>
#include <QTimer>
#include <QDragEnterEvent>
#if QT_VERSION < 0x050000
@@ -1368,7 +1368,7 @@ void TrianglesGUI::encryptWallet(bool status)
void TrianglesGUI::backupWallet()
{
QString saveDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation);
QString saveDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
QString filename = QFileDialog::getSaveFileName(this, tr("Backup Wallet"), saveDir, tr("Wallet Data (*.dat)"));
if(!filename.isEmpty()) {
if(!walletModel->backupWallet(filename)) {
+1 -1
View File
@@ -28,7 +28,7 @@ leveldb::DB *txdb; // global pointer for LevelDB object instance
static leveldb::Options GetOptions() {
leveldb::Options options;
int nCacheSizeMB = GetArg("-dbcache", 25);
int nCacheSizeMB = GetArg("-dbcache", 128);
options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576);
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
return options;
+79 -33
View File
@@ -33,6 +33,36 @@ struct CompareValueOnly
}
};
static CBlockIndex* GetWalletRescanStart(const CWallet& wallet)
{
CBlockIndex* pindexStart = pindexGenesisBlock;
if (wallet.fFileBacked)
{
CWalletDB walletdb(wallet.strWalletFile);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator))
{
CBlockIndex* pindexLocator = locator.GetBlockIndex();
if (pindexLocator)
pindexStart = pindexLocator;
}
}
if (wallet.nTimeFirstKey > 1 && pindexBest)
{
int64_t nTimeWindowStart = wallet.nTimeFirstKey - 7200;
CBlockIndex* pindexBirthday = pindexBest;
while (pindexBirthday->pprev && pindexBirthday->GetBlockTime() > nTimeWindowStart)
pindexBirthday = pindexBirthday->pprev;
if (!pindexStart || pindexBirthday->nHeight < pindexStart->nHeight)
pindexStart = pindexBirthday;
}
return pindexStart ? pindexStart : pindexGenesisBlock;
}
CPubKey CWallet::GenerateNewKey()
{
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
@@ -875,6 +905,9 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
LOCK(cs_wallet);
while (pindex)
{
if (fShutdown)
break;
// no need to read and scan block, if block was created before
// our wallet birthday (as adjusted for block time variability)
if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) {
@@ -910,54 +943,67 @@ void CWallet::ReacceptWalletTransactions()
bool fRepeat = true;
while (fRepeat)
{
LOCK(cs_wallet);
if (fShutdown)
break;
fRepeat = false;
vector<CDiskTxPos> vMissingTx;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
continue;
CTxIndex txindex;
bool fUpdated = false;
if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
LOCK(cs_wallet);
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
if (txindex.vSpent.size() != wtx.vout.size())
{
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %"PRIszu" != wtx.vout.size() %"PRIszu"\n", txindex.vSpent.size(), wtx.vout.size());
if (fShutdown)
break;
CWalletTx& wtx = item.second;
if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
continue;
}
for (unsigned int i = 0; i < txindex.vSpent.size(); i++)
CTxIndex txindex;
bool fUpdated = false;
if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
{
if (wtx.IsSpent(i))
continue;
if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i]))
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
if (txindex.vSpent.size() != wtx.vout.size())
{
wtx.MarkSpent(i);
fUpdated = true;
vMissingTx.push_back(txindex.vSpent[i]);
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %"PRIszu" != wtx.vout.size() %"PRIszu"\n", txindex.vSpent.size(), wtx.vout.size());
continue;
}
for (unsigned int i = 0; i < txindex.vSpent.size(); i++)
{
if (wtx.IsSpent(i))
continue;
if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i]))
{
wtx.MarkSpent(i);
fUpdated = true;
vMissingTx.push_back(txindex.vSpent[i]);
}
}
if (fUpdated)
{
printf("ReacceptWalletTransactions found spent coin %s SUM %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkDirty();
wtx.WriteToDisk();
}
}
if (fUpdated)
else
{
printf("ReacceptWalletTransactions found spent coin %s SUM %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkDirty();
wtx.WriteToDisk();
// Re-accept any txes of ours that aren't already in a block
if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
wtx.AcceptWalletTransaction(txdb);
}
}
else
{
// Re-accept any txes of ours that aren't already in a block
if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
wtx.AcceptWalletTransaction(txdb);
}
}
if (!vMissingTx.empty())
{
// TODO: optimize this to scan just part of the block chain?
if (ScanForWalletTransactions(pindexGenesisBlock))
if (fShutdown)
break;
CBlockIndex* pindexStart = GetWalletRescanStart(*this);
if (pindexStart)
printf("ReacceptWalletTransactions rescanning from block %d\n", pindexStart->nHeight);
if (ScanForWalletTransactions(pindexStart))
fRepeat = true; // Found missing transactions: re-do re-accept.
}
}
+5 -5
View File
@@ -7,13 +7,13 @@ DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE BOOST_THREAD_PROV
CONFIG += no_include_pwd
CONFIG += thread
QMAKE_CXXFLAGS = -fpermissive
greaterThan(QT_MAJOR_VERSION, 4) {
QT += widgets
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
lessThan(QT_MAJOR_VERSION, 5) {
error(Triangles Qt requires Qt 5 or newer)
}
QT += widgets
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
# UNCOMMENT THIS SECTION TO BUILD ON WINDOWS
# MSYS2 MinGW64 paths
win32 {