Remove mutexes
This commit is contained in:
committed by
Greg Fitzgerald
parent
304f152315
commit
e83d76fbd9
96
src/bank.rs
96
src/bank.rs
@ -18,6 +18,8 @@ use std::result;
|
|||||||
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering};
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use transaction::{Instruction, Plan, Transaction};
|
use transaction::{Instruction, Plan, Transaction};
|
||||||
|
use std::time::Instant;
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
/// The number of most recent `last_id` values that the bank will track the signatures
|
/// The number of most recent `last_id` values that the bank will track the signatures
|
||||||
/// of. Once the bank discards a `last_id`, it will reject any transactions that use
|
/// of. Once the bank discards a `last_id`, it will reject any transactions that use
|
||||||
@ -57,8 +59,10 @@ pub type Result<T> = result::Result<T, BankError>;
|
|||||||
/// The state of all accounts and contracts after processing its entries.
|
/// The state of all accounts and contracts after processing its entries.
|
||||||
pub struct Bank {
|
pub struct Bank {
|
||||||
/// A map of account public keys to the balance in that account.
|
/// A map of account public keys to the balance in that account.
|
||||||
balances: RwLock<HashMap<PublicKey, AtomicIsize>>,
|
balances: RwLock<HashMap<PublicKey, i64>>,
|
||||||
//balances: RwLock<HashMap<PublicKey, RwLock<i64>>>,
|
//balances: RwLock<HashMap<PublicKey, RwLock<i64>>>,
|
||||||
|
//balances: RwLock<HashMap<PublicKey, AtomicIsize>>,
|
||||||
|
|
||||||
|
|
||||||
/// A map of smart contract transaction signatures to what remains of its payment
|
/// A map of smart contract transaction signatures to what remains of its payment
|
||||||
/// plan. Each transaction that targets the plan should cause it to be reduced.
|
/// plan. Each transaction that targets the plan should cause it to be reduced.
|
||||||
@ -97,7 +101,7 @@ impl Bank {
|
|||||||
last_time: RwLock::new(Utc.timestamp(0, 0)),
|
last_time: RwLock::new(Utc.timestamp(0, 0)),
|
||||||
transaction_count: AtomicUsize::new(0),
|
transaction_count: AtomicUsize::new(0),
|
||||||
};
|
};
|
||||||
bank.apply_payment(deposit);
|
bank.apply_payment(deposit, &mut bank.balances.write().unwrap());
|
||||||
bank
|
bank
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,31 +116,44 @@ impl Bank {
|
|||||||
bank
|
bank
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Commit funds to the `payment.to` party.
|
fn apply_payment(&self, payment: &Payment, balances: &mut HashMap<PublicKey, i64>) {
|
||||||
fn apply_payment(&self, payment: &Payment) {
|
if balances.contains_key(&payment.to) {
|
||||||
|
*balances.get_mut(&payment.to).unwrap() += payment.tokens;
|
||||||
|
} else {
|
||||||
|
balances.insert(payment.to, payment.tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit funds to the 'to' party.
|
||||||
|
/*fn apply_payment(&self, payment: &Payment) {
|
||||||
// First we check balances with a read lock to maximize potential parallelization.
|
// First we check balances with a read lock to maximize potential parallelization.
|
||||||
if self.balances
|
if self.balances
|
||||||
.read()
|
.read()
|
||||||
.expect("'balances' read lock in apply_payment")
|
.expect("'balances' read lock in apply_payment")
|
||||||
.contains_key(&payment.to)
|
.contains_key(&payment.to)
|
||||||
{
|
{
|
||||||
let bals = self.balances.read().expect("'balances' read lock");
|
let mut bals = self.balances.write().expect("'balances' read lock");
|
||||||
//bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
|
//bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
|
||||||
*bals[&payment.to].write().unwrap() += payment.tokens;
|
// *bals[&payment.to].write().unwrap() += payment.tokens;
|
||||||
|
let x = bals.get_mut(&payment.to).unwrap();
|
||||||
|
*x += payment.tokens;
|
||||||
//trace!("updated balance to {}", bals[&payment.to].load(Ordering::Relaxed));
|
//trace!("updated balance to {}", bals[&payment.to].load(Ordering::Relaxed));
|
||||||
} else {
|
} else {
|
||||||
// Now we know the key wasn't present a nanosecond ago, but it might be there
|
// Now we know the key wasn't present a nanosecond ago, but it might be there
|
||||||
// by the time we aquire a write lock, so we'll have to check again.
|
// by the time we aquire a write lock, so we'll have to check again.
|
||||||
let mut bals = self.balances.write().expect("'balances' write lock");
|
let mut bals = self.balances.write().expect("'balances' write lock");
|
||||||
if bals.contains_key(&payment.to) {
|
if bals.contains_key(&payment.to) {
|
||||||
*bals[&payment.to].write().unwrap() += payment.tokens;
|
let x= bals.get_mut(&payment.to).unwrap();
|
||||||
|
*x += payment.tokens;
|
||||||
|
// *bals[&payment.to].write().unwrap() += payment.tokens;
|
||||||
//bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
|
//bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
|
||||||
} else {
|
} else {
|
||||||
//bals.insert(payment.to, AtomicIsize::new(payment.tokens as isize));
|
//bals.insert(payment.to, AtomicIsize::new(payment.tokens as isize));
|
||||||
bals.insert(payment.to, RwLock::new(payment.tokens));
|
//bals.insert(payment.to, RwLock::new(payment.tokens));
|
||||||
|
bals.insert(payment.to, payment.tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/// Return the last entry ID registered.
|
/// Return the last entry ID registered.
|
||||||
pub fn last_id(&self) -> Hash {
|
pub fn last_id(&self) -> Hash {
|
||||||
@ -209,21 +226,26 @@ impl Bank {
|
|||||||
last_ids.push_back((*last_id, RwLock::new(HashSet::new())));
|
last_ids.push_back((*last_id, RwLock::new(HashSet::new())));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_debits(&self, tr: &Transaction) -> Result<()> {
|
pub fn apply_debits(&self, tr: &Transaction, bals: &mut HashMap<PublicKey, i64>) -> Result<()> {
|
||||||
|
|
||||||
let bals = self.balances.read().unwrap();
|
//let mut bals = self.balances.write().unwrap();
|
||||||
|
|
||||||
// Hold a write lock before the condition check, so that a debit can't occur
|
// Hold a write lock before the condition check, so that a debit can't occur
|
||||||
// between checking the balance and the withdraw.
|
// between checking the balance and the withdraw.
|
||||||
let option = bals.get(&tr.from);
|
let mut option = bals.get_mut(&tr.from);
|
||||||
if option.is_none() {
|
if option.is_none() {
|
||||||
return Err(BankError::AccountNotFound(tr.from));
|
return Err(BankError::AccountNotFound(tr.from));
|
||||||
}
|
}
|
||||||
let mut bal = option.unwrap().write().unwrap();
|
//let mut bal = option.unwrap().write().unwrap();
|
||||||
|
let mut bal = option.unwrap();
|
||||||
|
|
||||||
self.reserve_signature_with_last_id(&tr.sig, &tr.last_id)?;
|
self.reserve_signature_with_last_id(&tr.sig, &tr.last_id)?;
|
||||||
|
|
||||||
if let Instruction::NewContract(contract) = &tr.instruction {
|
if let Instruction::NewContract(contract) = &tr.instruction {
|
||||||
|
if contract.tokens < 0 {
|
||||||
|
return Err(BankError::NegativeTokens);
|
||||||
|
}
|
||||||
|
|
||||||
if *bal < contract.tokens {
|
if *bal < contract.tokens {
|
||||||
self.forget_signature_with_last_id(&tr.sig, &tr.last_id);
|
self.forget_signature_with_last_id(&tr.sig, &tr.last_id);
|
||||||
return Err(BankError::InsufficientFunds(tr.from));
|
return Err(BankError::InsufficientFunds(tr.from));
|
||||||
@ -284,9 +306,7 @@ impl Bank {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
/// Apply only a transaction's credits. Credits from multiple transactions
|
fn apply_credits(&self, tx: &Transaction, balances: &mut HashMap<PublicKey, i64>) {
|
||||||
/// may safely be applied in parallel.
|
|
||||||
fn apply_credits(&self, tx: &Transaction) {
|
|
||||||
match &tx.instruction {
|
match &tx.instruction {
|
||||||
Instruction::NewContract(contract) => {
|
Instruction::NewContract(contract) => {
|
||||||
let mut plan = contract.plan.clone();
|
let mut plan = contract.plan.clone();
|
||||||
@ -295,7 +315,7 @@ impl Bank {
|
|||||||
.expect("timestamp creation in apply_credits")));
|
.expect("timestamp creation in apply_credits")));
|
||||||
|
|
||||||
if let Some(payment) = plan.final_payment() {
|
if let Some(payment) = plan.final_payment() {
|
||||||
self.apply_payment(&payment);
|
self.apply_payment(&payment, balances);
|
||||||
} else {
|
} else {
|
||||||
let mut pending = self.pending
|
let mut pending = self.pending
|
||||||
.write()
|
.write()
|
||||||
@ -315,28 +335,45 @@ impl Bank {
|
|||||||
/// Process a Transaction. If it contains a payment plan that requires a witness
|
/// Process a Transaction. If it contains a payment plan that requires a witness
|
||||||
/// to progress, the payment plan will be stored in the bank.
|
/// to progress, the payment plan will be stored in the bank.
|
||||||
fn process_transaction(&self, tx: &Transaction) -> Result<()> {
|
fn process_transaction(&self, tx: &Transaction) -> Result<()> {
|
||||||
self.apply_debits(tx)?;
|
let bals = &mut self.balances.write().unwrap();
|
||||||
self.apply_credits(tx);
|
self.apply_debits(tx, bals)?;
|
||||||
|
self.apply_credits(tx, bals);
|
||||||
|
self.transaction_count.fetch_add(1, Ordering::Relaxed);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process a batch of transactions. It runs all debits first to filter out any
|
/// Process a batch of transactions. It runs all debits first to filter out any
|
||||||
/// transactions that can't be processed in parallel deterministically.
|
/// transactions that can't be processed in parallel deterministically.
|
||||||
pub fn process_transactions(&self, txs: Vec<Transaction>) -> Vec<Result<Transaction>> {
|
pub fn process_transactions(&self, txs: Vec<Transaction>) -> Vec<Result<Transaction>> {
|
||||||
debug!("processing Transactions {}", txs.len());
|
// Run all debits first to filter out any transactions that can't be processed
|
||||||
let results: Vec<_> = txs.into_par_iter()
|
// in parallel deterministically.
|
||||||
.map(|tx| self.apply_debits(&tx).map(|_| tx))
|
let bals = &mut self.balances.write().unwrap();
|
||||||
|
info!("processing Transactions {}", txs.len());
|
||||||
|
let now = Instant::now();
|
||||||
|
let results: Vec<_> = txs.into_iter()
|
||||||
|
.map(|tx| self.apply_debits(&tx, bals).map(|_| tx))
|
||||||
.collect(); // Calling collect() here forces all debits to complete before moving on.
|
.collect(); // Calling collect() here forces all debits to complete before moving on.
|
||||||
|
|
||||||
results
|
info!("debits: {:?}", now.elapsed());
|
||||||
.into_par_iter()
|
|
||||||
|
let res: Vec<_> = results
|
||||||
|
.into_iter()
|
||||||
.map(|result| {
|
.map(|result| {
|
||||||
result.map(|tx| {
|
result.map(|tx| {
|
||||||
self.apply_credits(&tx);
|
self.apply_credits(&tx, bals);
|
||||||
tx
|
tx
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect();
|
||||||
|
let mut tr_count = 0;
|
||||||
|
for r in &res {
|
||||||
|
if r.is_ok() {
|
||||||
|
tr_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.transaction_count.fetch_add(tr_count, Ordering::Relaxed);
|
||||||
|
info!("credits: {:?}", now.elapsed());
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process an ordered list of entries.
|
/// Process an ordered list of entries.
|
||||||
@ -365,7 +402,7 @@ impl Bank {
|
|||||||
{
|
{
|
||||||
e.get_mut().apply_witness(&Witness::Signature(from));
|
e.get_mut().apply_witness(&Witness::Signature(from));
|
||||||
if let Some(payment) = e.get().final_payment() {
|
if let Some(payment) = e.get().final_payment() {
|
||||||
self.apply_payment(&payment);
|
self.apply_payment(&payment, &mut self.balances.write().unwrap());
|
||||||
e.remove_entry();
|
e.remove_entry();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -414,7 +451,7 @@ impl Bank {
|
|||||||
.read()
|
.read()
|
||||||
.expect("'last_time' read lock when creating timestamp")));
|
.expect("'last_time' read lock when creating timestamp")));
|
||||||
if let Some(payment) = plan.final_payment() {
|
if let Some(payment) = plan.final_payment() {
|
||||||
self.apply_payment(&payment);
|
self.apply_payment(&payment, &mut self.balances.write().unwrap());
|
||||||
completed.push(key.clone());
|
completed.push(key.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -461,7 +498,8 @@ impl Bank {
|
|||||||
.read()
|
.read()
|
||||||
.expect("'balances' read lock in get_balance");
|
.expect("'balances' read lock in get_balance");
|
||||||
//bals.get(pubkey).map(|x| x.load(Ordering::Relaxed) as i64)
|
//bals.get(pubkey).map(|x| x.load(Ordering::Relaxed) as i64)
|
||||||
bals.get(pubkey).map(|x| *x.read().unwrap())
|
//bals.get(pubkey).map(|x| *x.read().unwrap())
|
||||||
|
bals.get(pubkey).map(|x| *x)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transaction_count(&self) -> usize {
|
pub fn transaction_count(&self) -> usize {
|
||||||
|
Reference in New Issue
Block a user