Skip to content

Commit c37851d

Browse files
committed
rpc: Make unloadwallet wait for complete wallet unload
1 parent e7b88ec commit c37851d

File tree

5 files changed

+56
-12
lines changed

5 files changed

+56
-12
lines changed

src/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,10 @@ void Shutdown(InitInterfaces& interfaces)
262262
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
263263
}
264264
#endif
265+
interfaces.chain_clients.clear();
265266
UnregisterAllValidationInterfaces();
266267
GetMainSignals().UnregisterBackgroundSignalScheduler();
267268
GetMainSignals().UnregisterWithMempoolSignals(mempool);
268-
interfaces.chain_clients.clear();
269269
globalVerifyHandle.reset();
270270
ECC_Stop();
271271
LogPrintf("%s: done\n", __func__);

src/wallet/init.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,11 @@ void StopWallets()
242242

243243
void UnloadWallets()
244244
{
245-
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
246-
RemoveWallet(pwallet);
245+
auto wallets = GetWallets();
246+
while (!wallets.empty()) {
247+
auto wallet = wallets.back();
248+
wallets.pop_back();
249+
RemoveWallet(wallet);
250+
UnloadWallet(std::move(wallet));
247251
}
248252
}

src/wallet/rpcwallet.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2625,16 +2625,8 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
26252625
if (!RemoveWallet(wallet)) {
26262626
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
26272627
}
2628-
UnregisterValidationInterface(wallet.get());
26292628

2630-
// The wallet can be in use so it's not possible to explicitly unload here.
2631-
// Just notify the unload intent so that all shared pointers are released.
2632-
// The wallet will be destroyed once the last shared pointer is released.
2633-
wallet->NotifyUnload();
2634-
2635-
// There's no point in waiting for the wallet to unload.
2636-
// At this point this method should never fail. The unloading could only
2637-
// fail due to an unexpected error which would cause a process termination.
2629+
UnloadWallet(std::move(wallet));
26382630

26392631
return NullUniValue;
26402632
}

src/wallet/wallet.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,52 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name)
8282
return nullptr;
8383
}
8484

85+
static Mutex g_wallet_release_mutex;
86+
static std::condition_variable g_wallet_release_cv;
87+
static std::set<CWallet*> g_unloading_wallet_set;
88+
8589
// Custom deleter for shared_ptr<CWallet>.
8690
static void ReleaseWallet(CWallet* wallet)
8791
{
92+
// Unregister and delete the wallet right after BlockUntilSyncedToCurrentChain
93+
// so that it's in sync with the current chainstate.
8894
wallet->WalletLogPrintf("Releasing wallet\n");
8995
wallet->BlockUntilSyncedToCurrentChain();
9096
wallet->Flush();
97+
UnregisterValidationInterface(wallet);
9198
delete wallet;
99+
// Wallet is now released, notify UnloadWallet, if any.
100+
{
101+
LOCK(g_wallet_release_mutex);
102+
if (g_unloading_wallet_set.erase(wallet) == 0) {
103+
// UnloadWallet was not called for this wallet, all done.
104+
return;
105+
}
106+
}
107+
g_wallet_release_cv.notify_all();
108+
}
109+
110+
void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
111+
{
112+
// Mark wallet for unloading.
113+
CWallet* pwallet = wallet.get();
114+
{
115+
LOCK(g_wallet_release_mutex);
116+
auto it = g_unloading_wallet_set.insert(pwallet);
117+
assert(it.second);
118+
}
119+
// The wallet can be in use so it's not possible to explicitly unload here.
120+
// Notify the unload intent so that all remaining shared pointers are
121+
// released.
122+
pwallet->NotifyUnload();
123+
// Time to ditch our shared_ptr and wait for ReleaseWallet call.
124+
wallet.reset();
125+
{
126+
WAIT_LOCK(g_wallet_release_mutex, lock);
127+
while (g_unloading_wallet_set.count(pwallet) == 1) {
128+
g_wallet_release_cv.wait(lock);
129+
}
130+
}
92131
}
93132

94133
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;

src/wallet/wallet.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ void StopWallets();
5454
//! Close all wallets.
5555
void UnloadWallets();
5656

57+
//! Explicitly unload and delete the wallet.
58+
// Blocks the current thread after signaling the unload intent so that all
59+
// wallet clients release the wallet.
60+
// Note that, when blocking is not required, the wallet is implicitly unloaded
61+
// by the shared pointer deleter.
62+
void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
63+
5764
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
5865
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
5966
bool HasWallets();
@@ -770,6 +777,8 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
770777

771778
~CWallet()
772779
{
780+
// Should not have slots connected at this point.
781+
assert(NotifyUnload.empty());
773782
delete encrypted_batch;
774783
encrypted_batch = nullptr;
775784
}

0 commit comments

Comments
 (0)