Implement finalizer so that all locked accounts are dropped (#3585)

* Implement finalizer so that all locked accounts are dropped when finalizer goes out of scope

* Add test for tx error with lock conflict

* Fix double unlock from destructor running after a call to unlock
This commit is contained in:
carllin
2019-04-02 03:55:42 -07:00
committed by GitHub
parent 92c66a411b
commit d228b6467c
5 changed files with 162 additions and 30 deletions

View File

@ -5,6 +5,7 @@
use crate::accounts::{Accounts, ErrorCounters, InstructionAccounts, InstructionLoaders};
use crate::blockhash_queue::BlockhashQueue;
use crate::locked_accounts_results::LockedAccountsResults;
use crate::runtime::{ProcessInstruction, Runtime};
use crate::status_cache::StatusCache;
use bincode::serialize;
@ -467,18 +468,28 @@ impl Bank {
.map_or(Ok(()), |sig| self.get_signature_status(sig).unwrap())
}
pub fn lock_accounts(&self, txs: &[Transaction]) -> Vec<Result<()>> {
pub fn lock_accounts<'a, 'b>(
&'a self,
txs: &'b [Transaction],
) -> LockedAccountsResults<'a, 'b> {
if self.is_frozen() {
warn!("=========== FIXME: lock_accounts() working on a frozen bank! ================");
}
// TODO: put this assert back in
// assert!(!self.is_frozen());
self.accounts.lock_accounts(self.accounts_id, txs)
let results = self.accounts.lock_accounts(self.accounts_id, txs);
LockedAccountsResults::new(results, &self, txs)
}
pub fn unlock_accounts(&self, txs: &[Transaction], results: &[Result<()>]) {
self.accounts
.unlock_accounts(self.accounts_id, txs, results)
pub fn unlock_accounts(&self, locked_accounts_results: &mut LockedAccountsResults) {
if locked_accounts_results.needs_unlock {
locked_accounts_results.needs_unlock = false;
self.accounts.unlock_accounts(
self.accounts_id,
locked_accounts_results.transactions(),
locked_accounts_results.locked_accounts_results(),
)
}
}
fn load_accounts(
@ -498,17 +509,17 @@ impl Bank {
fn check_refs(
&self,
txs: &[Transaction],
lock_results: Vec<Result<()>>,
lock_results: &LockedAccountsResults,
error_counters: &mut ErrorCounters,
) -> Vec<Result<()>> {
txs.iter()
.zip(lock_results.into_iter())
.zip(lock_results.locked_accounts_results())
.map(|(tx, lock_res)| {
if lock_res.is_ok() && !tx.verify_refs() {
error_counters.invalid_account_index += 1;
Err(TransactionError::InvalidAccountIndex)
} else {
lock_res
lock_res.clone()
}
})
.collect()
@ -575,7 +586,7 @@ impl Bank {
pub fn load_and_execute_transactions(
&self,
txs: &[Transaction],
lock_results: Vec<Result<()>>,
lock_results: &LockedAccountsResults,
max_age: usize,
) -> (
Vec<Result<(InstructionAccounts, InstructionLoaders)>>,
@ -741,7 +752,7 @@ impl Bank {
pub fn load_execute_and_commit_transactions(
&self,
txs: &[Transaction],
lock_results: Vec<Result<()>>,
lock_results: &LockedAccountsResults,
max_age: usize,
) -> Vec<Result<()>> {
let (loaded_accounts, executed) =
@ -753,10 +764,7 @@ impl Bank {
#[must_use]
pub fn process_transactions(&self, txs: &[Transaction]) -> Vec<Result<()>> {
let lock_results = self.lock_accounts(txs);
let results =
self.load_execute_and_commit_transactions(txs, lock_results, MAX_RECENT_BLOCKHASHES);
self.unlock_accounts(txs, &results);
results
self.load_execute_and_commit_transactions(txs, &lock_results, MAX_RECENT_BLOCKHASHES)
}
/// Create, sign, and process a Transaction from `keypair` to `to` of
@ -1312,7 +1320,7 @@ mod tests {
let lock_result = bank.lock_accounts(&pay_alice);
let results_alice = bank.load_execute_and_commit_transactions(
&pay_alice,
lock_result,
&lock_result,
MAX_RECENT_BLOCKHASHES,
);
assert_eq!(results_alice[0], Ok(()));
@ -1329,7 +1337,7 @@ mod tests {
Err(TransactionError::AccountInUse)
);
bank.unlock_accounts(&pay_alice, &results_alice);
drop(lock_result);
assert!(bank.transfer(2, &mint_keypair, &bob.pubkey()).is_ok());
}

View File

@ -5,6 +5,7 @@ pub mod bank_client;
mod blockhash_queue;
pub mod bloom;
pub mod loader_utils;
pub mod locked_accounts_results;
mod native_loader;
pub mod runtime;
mod status_cache;

View File

@ -0,0 +1,42 @@
use crate::bank::{Bank, Result};
use solana_sdk::transaction::Transaction;
// Represents the results of trying to lock a set of accounts
pub struct LockedAccountsResults<'a, 'b> {
locked_accounts_results: Vec<Result<()>>,
bank: &'a Bank,
transactions: &'b [Transaction],
pub(crate) needs_unlock: bool,
}
impl<'a, 'b> LockedAccountsResults<'a, 'b> {
pub fn new(
locked_accounts_results: Vec<Result<()>>,
bank: &'a Bank,
transactions: &'b [Transaction],
) -> Self {
Self {
locked_accounts_results,
bank,
transactions,
needs_unlock: true,
}
}
pub fn locked_accounts_results(&self) -> &Vec<Result<()>> {
&self.locked_accounts_results
}
pub fn transactions(&self) -> &[Transaction] {
self.transactions
}
}
// Unlock all locked accounts in destructor.
impl<'a, 'b> Drop for LockedAccountsResults<'a, 'b> {
fn drop(&mut self) {
if self.needs_unlock {
self.bank.unlock_accounts(self)
}
}
}