diff --git a/src/wallet/rpc/encrypt.cpp b/src/wallet/rpc/encrypt.cpp index 7863708573..a5f1cea91e 100644 --- a/src/wallet/rpc/encrypt.cpp +++ b/src/wallet/rpc/encrypt.cpp @@ -3,6 +3,8 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include +#include #include #include @@ -88,24 +90,24 @@ RPCHelpMan walletpassphrase() relock_time = pwallet->nRelockTime; } - // rpcRunLater must be called without cs_wallet held otherwise a deadlock - // can occur. The deadlock would happen when RPCRunLater removes the - // previous timer (and waits for the callback to finish if already running) - // and the callback locks cs_wallet. - AssertLockNotHeld(wallet->cs_wallet); + // Get wallet scheduler to queue up the relock callback in the future. + // Scheduled events don't get destructed until they are executed, + // and they are executed in series in a single scheduler thread so + // no cs_wallet lock is needed. + WalletContext& context = EnsureWalletContext(request.context); // Keep a weak pointer to the wallet so that it is possible to unload the // wallet before the following callback is called. If a valid shared pointer // is acquired in the callback then the wallet is still loaded. std::weak_ptr weak_wallet = wallet; - pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] { + context.scheduler->scheduleFromNow([weak_wallet, relock_time] { if (auto shared_wallet = weak_wallet.lock()) { LOCK2(shared_wallet->m_relock_mutex, shared_wallet->cs_wallet); - // Skip if this is not the most recent rpcRunLater callback. + // Skip if this is not the most recent relock callback. if (shared_wallet->nRelockTime != relock_time) return; shared_wallet->Lock(); shared_wallet->nRelockTime = 0; } - }, nSleepTime); + }, std::chrono::seconds(nSleepTime)); return UniValue::VNULL; }, diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 78c0426e3a..03fed4dfbf 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the wallet keypool and interaction with wallet encryption/locking.""" -import time from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework @@ -127,8 +126,10 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0].keypoolrefill(3) # test walletpassphrase timeout - time.sleep(1.1) - assert_equal(nodes[0].getwalletinfo()["unlocked_until"], 0) + # CScheduler relies on condition_variable::wait_until() which does not + # guarantee accurate timing. We'll wait up to 5 seconds to execute a 1 + # second scheduled event. + nodes[0].wait_until(lambda: nodes[0].getwalletinfo()["unlocked_until"] == 0, timeout=5) # drain the keypool for _ in range(3):