Perform wallet representative action without holding any mutex (#2759)
* Perform wallet representative action without holding any mutex This relaxes restrictions on the action and avoids potential deadlocks through lock-order-inversion * Relax debug_assert when adding to votes_cache as the representative counts can change even during a foreach_representative action * Windows requires explicitly unlocking the mutex once locked
This commit is contained in:
parent
6b89f7090d
commit
f30b3e87dd
3 changed files with 57 additions and 28 deletions
|
|
@ -1556,3 +1556,22 @@ TEST (wallet, epoch_2_receive_unopened)
|
|||
}
|
||||
ASSERT_LT (tries, max_tries);
|
||||
}
|
||||
|
||||
TEST (wallet, foreach_representative_deadlock)
|
||||
{
|
||||
nano::system system (1);
|
||||
auto & node (*system.nodes[0]);
|
||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||
node.wallets.compute_reps ();
|
||||
ASSERT_EQ (1, node.wallets.rep_counts ().voting);
|
||||
node.wallets.foreach_representative ([&node](nano::public_key const & pub, nano::raw_key const & prv) {
|
||||
if (node.wallets.mutex.try_lock ())
|
||||
{
|
||||
node.wallets.mutex.unlock ();
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT_FALSE (true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,9 +137,12 @@ wallets (wallets_a)
|
|||
|
||||
void nano::votes_cache::add (std::shared_ptr<nano::vote> const & vote_a)
|
||||
{
|
||||
nano::lock_guard<std::mutex> lock (cache_mutex);
|
||||
auto voting (wallets.rep_counts ().voting);
|
||||
debug_assert (voting > 0);
|
||||
if (voting == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nano::lock_guard<std::mutex> lock (cache_mutex);
|
||||
auto const max_cache_size (network_params.voting.max_cache / std::max (voting, static_cast<decltype (voting)> (1)));
|
||||
for (auto & block : vote_a->blocks)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1750,44 +1750,51 @@ void nano::wallets::foreach_representative (std::function<void(nano::public_key
|
|||
{
|
||||
if (node.config.enable_voting)
|
||||
{
|
||||
nano::lock_guard<std::mutex> lock (mutex);
|
||||
auto transaction_l (tx_begin_read ());
|
||||
for (auto i (items.begin ()), n (items.end ()); i != n; ++i)
|
||||
std::vector<std::pair<nano::public_key const, nano::raw_key const>> action_accounts_l;
|
||||
{
|
||||
auto & wallet (*i->second);
|
||||
nano::lock_guard<std::recursive_mutex> store_lock (wallet.store.mutex);
|
||||
decltype (wallet.representatives) representatives_l;
|
||||
auto transaction_l (tx_begin_read ());
|
||||
nano::lock_guard<std::mutex> lock (mutex);
|
||||
for (auto i (items.begin ()), n (items.end ()); i != n; ++i)
|
||||
{
|
||||
nano::lock_guard<std::mutex> representatives_lock (wallet.representatives_mutex);
|
||||
representatives_l = wallet.representatives;
|
||||
}
|
||||
for (auto const & account : representatives_l)
|
||||
{
|
||||
if (wallet.store.exists (transaction_l, account))
|
||||
auto & wallet (*i->second);
|
||||
nano::lock_guard<std::recursive_mutex> store_lock (wallet.store.mutex);
|
||||
decltype (wallet.representatives) representatives_l;
|
||||
{
|
||||
if (!node.ledger.weight (account).is_zero ())
|
||||
nano::lock_guard<std::mutex> representatives_lock (wallet.representatives_mutex);
|
||||
representatives_l = wallet.representatives;
|
||||
}
|
||||
for (auto const & account : representatives_l)
|
||||
{
|
||||
if (wallet.store.exists (transaction_l, account))
|
||||
{
|
||||
if (wallet.store.valid_password (transaction_l))
|
||||
if (!node.ledger.weight (account).is_zero ())
|
||||
{
|
||||
nano::raw_key prv;
|
||||
auto error (wallet.store.fetch (transaction_l, account, prv));
|
||||
(void)error;
|
||||
debug_assert (!error);
|
||||
action_a (account, prv);
|
||||
}
|
||||
else
|
||||
{
|
||||
static auto last_log = std::chrono::steady_clock::time_point ();
|
||||
if (last_log < std::chrono::steady_clock::now () - std::chrono::seconds (60))
|
||||
if (wallet.store.valid_password (transaction_l))
|
||||
{
|
||||
last_log = std::chrono::steady_clock::now ();
|
||||
node.logger.always_log (boost::str (boost::format ("Representative locked inside wallet %1%") % i->first.to_string ()));
|
||||
nano::raw_key prv;
|
||||
auto error (wallet.store.fetch (transaction_l, account, prv));
|
||||
(void)error;
|
||||
debug_assert (!error);
|
||||
action_accounts_l.emplace_back (account, prv);
|
||||
}
|
||||
else
|
||||
{
|
||||
static auto last_log = std::chrono::steady_clock::time_point ();
|
||||
if (last_log < std::chrono::steady_clock::now () - std::chrono::seconds (60))
|
||||
{
|
||||
last_log = std::chrono::steady_clock::now ();
|
||||
node.logger.always_log (boost::str (boost::format ("Representative locked inside wallet %1%") % i->first.to_string ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto const & representative : action_accounts_l)
|
||||
{
|
||||
action_a (representative.first, representative.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue