aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoão Barbosa <joao.paulo.barbosa@gmail.com>2018-12-12 23:21:19 +0000
committerJoão Barbosa <joao.paulo.barbosa@gmail.com>2019-01-15 00:01:00 +0000
commitc37851de5752f107c16e19317f28038b6b7ca2dc (patch)
tree9630a18acef413ea7fed27268037db1a84ead1f5
parente7b88ecbc920321290941bc68e4a71634889c3cb (diff)
rpc: Make unloadwallet wait for complete wallet unload
-rw-r--r--src/init.cpp2
-rw-r--r--src/wallet/init.cpp8
-rw-r--r--src/wallet/rpcwallet.cpp10
-rw-r--r--src/wallet/wallet.cpp39
-rw-r--r--src/wallet/wallet.h9
5 files changed, 56 insertions, 12 deletions
diff --git a/src/init.cpp b/src/init.cpp
index 8ecd79197f..4653f9c982 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -262,10 +262,10 @@ void Shutdown(InitInterfaces& interfaces)
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
}
#endif
+ interfaces.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
GetMainSignals().UnregisterWithMempoolSignals(mempool);
- interfaces.chain_clients.clear();
globalVerifyHandle.reset();
ECC_Stop();
LogPrintf("%s: done\n", __func__);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 14d811c6cd..87cd264c3d 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -242,7 +242,11 @@ void StopWallets()
void UnloadWallets()
{
- for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
- RemoveWallet(pwallet);
+ auto wallets = GetWallets();
+ while (!wallets.empty()) {
+ auto wallet = wallets.back();
+ wallets.pop_back();
+ RemoveWallet(wallet);
+ UnloadWallet(std::move(wallet));
}
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 1d9ab54857..87d6d9262c 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -2625,16 +2625,8 @@ static UniValue unloadwallet(const JSONRPCRequest& request)
if (!RemoveWallet(wallet)) {
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.
- // 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.
+ UnloadWallet(std::move(wallet));
return NullUniValue;
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 109f8e6da0..13e85b55f0 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -82,13 +82,52 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name)
return nullptr;
}
+static 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>.
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->BlockUntilSyncedToCurrentChain();
wallet->Flush();
+ UnregisterValidationInterface(wallet);
delete wallet;
+ // Wallet is now released, notify UnloadWallet, if any.
+ {
+ 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();
+ {
+ 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();
+ {
+ WAIT_LOCK(g_wallet_release_mutex, lock);
+ while (g_unloading_wallet_set.count(pwallet) == 1) {
+ g_wallet_release_cv.wait(lock);
+ }
+ }
}
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 95a2c833f8..fc88b1bae8 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -54,6 +54,13 @@ void StopWallets();
//! Close all wallets.
void UnloadWallets();
+//! 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 RemoveWallet(const std::shared_ptr<CWallet>& wallet);
bool HasWallets();
@@ -770,6 +777,8 @@ public:
~CWallet()
{
+ // Should not have slots connected at this point.
+ assert(NotifyUnload.empty());
delete encrypted_batch;
encrypted_batch = nullptr;
}