rpc: Make unloadwallet wait for complete wallet unload
Github-Pull: #14941
Rebased-From: c37851d
This commit is contained in:
parent
0a0b2ea11f
commit
0cd9ad208c
5 changed files with 56 additions and 12 deletions
|
@ -282,10 +282,10 @@ void Shutdown()
|
||||||
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
|
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
g_wallet_init_interface.Close();
|
||||||
UnregisterAllValidationInterfaces();
|
UnregisterAllValidationInterfaces();
|
||||||
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
||||||
GetMainSignals().UnregisterWithMempoolSignals(mempool);
|
GetMainSignals().UnregisterWithMempoolSignals(mempool);
|
||||||
g_wallet_init_interface.Close();
|
|
||||||
globalVerifyHandle.reset();
|
globalVerifyHandle.reset();
|
||||||
ECC_Stop();
|
ECC_Stop();
|
||||||
LogPrintf("%s: done\n", __func__);
|
LogPrintf("%s: done\n", __func__);
|
||||||
|
|
|
@ -264,7 +264,11 @@ void WalletInit::Stop() const
|
||||||
|
|
||||||
void WalletInit::Close() const
|
void WalletInit::Close() const
|
||||||
{
|
{
|
||||||
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
auto wallets = GetWallets();
|
||||||
RemoveWallet(pwallet);
|
while (!wallets.empty()) {
|
||||||
|
auto wallet = wallets.back();
|
||||||
|
wallets.pop_back();
|
||||||
|
RemoveWallet(wallet);
|
||||||
|
UnloadWallet(std::move(wallet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3239,16 +3239,8 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
|
||||||
if (!RemoveWallet(wallet)) {
|
if (!RemoveWallet(wallet)) {
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
|
throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
|
||||||
}
|
}
|
||||||
UnregisterValidationInterface(wallet.get());
|
|
||||||
|
|
||||||
// The wallet can be in use so it's not possible to explicitly unload here.
|
UnloadWallet(std::move(wallet));
|
||||||
// Just notify the unload intent so that all shared pointers are released.
|
|
||||||
// The wallet will be destroyed once the last shared pointer is released.
|
|
||||||
wallet->NotifyUnload();
|
|
||||||
|
|
||||||
// There's no point in waiting for the wallet to unload.
|
|
||||||
// At this point this method should never fail. The unloading could only
|
|
||||||
// fail due to an unexpected error which would cause a process termination.
|
|
||||||
|
|
||||||
return NullUniValue;
|
return NullUniValue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,13 +81,52 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::mutex g_wallet_release_mutex;
|
||||||
|
static std::condition_variable g_wallet_release_cv;
|
||||||
|
static std::set<CWallet*> g_unloading_wallet_set;
|
||||||
|
|
||||||
// Custom deleter for shared_ptr<CWallet>.
|
// Custom deleter for shared_ptr<CWallet>.
|
||||||
static void ReleaseWallet(CWallet* wallet)
|
static void ReleaseWallet(CWallet* wallet)
|
||||||
{
|
{
|
||||||
|
// Unregister and delete the wallet right after BlockUntilSyncedToCurrentChain
|
||||||
|
// so that it's in sync with the current chainstate.
|
||||||
wallet->WalletLogPrintf("Releasing wallet\n");
|
wallet->WalletLogPrintf("Releasing wallet\n");
|
||||||
wallet->BlockUntilSyncedToCurrentChain();
|
wallet->BlockUntilSyncedToCurrentChain();
|
||||||
wallet->Flush();
|
wallet->Flush();
|
||||||
|
UnregisterValidationInterface(wallet);
|
||||||
delete wallet;
|
delete wallet;
|
||||||
|
// Wallet is now released, notify UnloadWallet, if any.
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_wallet_release_mutex);
|
||||||
|
if (g_unloading_wallet_set.erase(wallet) == 0) {
|
||||||
|
// UnloadWallet was not called for this wallet, all done.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_wallet_release_cv.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
|
||||||
|
{
|
||||||
|
// Mark wallet for unloading.
|
||||||
|
CWallet* pwallet = wallet.get();
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_wallet_release_mutex);
|
||||||
|
auto it = g_unloading_wallet_set.insert(pwallet);
|
||||||
|
assert(it.second);
|
||||||
|
}
|
||||||
|
// The wallet can be in use so it's not possible to explicitly unload here.
|
||||||
|
// Notify the unload intent so that all remaining shared pointers are
|
||||||
|
// released.
|
||||||
|
pwallet->NotifyUnload();
|
||||||
|
// Time to ditch our shared_ptr and wait for ReleaseWallet call.
|
||||||
|
wallet.reset();
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(g_wallet_release_mutex);
|
||||||
|
while (g_unloading_wallet_set.count(pwallet) == 1) {
|
||||||
|
g_wallet_release_cv.wait(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
|
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
|
||||||
|
|
|
@ -33,6 +33,13 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
//! Explicitly unload and delete the wallet.
|
||||||
|
// Blocks the current thread after signaling the unload intent so that all
|
||||||
|
// wallet clients release the wallet.
|
||||||
|
// Note that, when blocking is not required, the wallet is implicitly unloaded
|
||||||
|
// by the shared pointer deleter.
|
||||||
|
void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
|
||||||
|
|
||||||
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
|
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
|
||||||
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
|
bool RemoveWallet(const std::shared_ptr<CWallet>& wallet);
|
||||||
bool HasWallets();
|
bool HasWallets();
|
||||||
|
@ -817,6 +824,8 @@ public:
|
||||||
|
|
||||||
~CWallet()
|
~CWallet()
|
||||||
{
|
{
|
||||||
|
// Should not have slots connected at this point.
|
||||||
|
assert(NotifyUnload.empty());
|
||||||
delete encrypted_batch;
|
delete encrypted_batch;
|
||||||
encrypted_batch = nullptr;
|
encrypted_batch = nullptr;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue