Make apply_payment a method

History: the function was pulled out of Bank when each field wasn't
wrapped in a RwLock, and that locking 'balances' meant to lock
everything in the bank. Now that the RwLocks are here to stay,
we can make it a method again.
This commit is contained in:
Greg Fitzgerald 2018-05-29 10:04:37 -06:00
parent 71cb7d5c97
commit 2570bba6b1
1 changed files with 30 additions and 30 deletions

View File

@ -30,28 +30,6 @@ pub enum BankError {
pub type Result<T> = result::Result<T, BankError>;
/// Commit funds to the 'to' party.
fn apply_payment(balances: &RwLock<HashMap<PublicKey, AtomicIsize>>, payment: &Payment) {
// First we check balances with a read lock to maximize potential parallelization.
if balances
.read()
.expect("'balances' read lock in apply_payment")
.contains_key(&payment.to)
{
let bals = balances.read().expect("'balances' read lock");
bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
} else {
// 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.
let mut bals = balances.write().expect("'balances' write lock");
if bals.contains_key(&payment.to) {
bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
} else {
bals.insert(payment.to, AtomicIsize::new(payment.tokens as isize));
}
}
}
pub struct Bank {
balances: RwLock<HashMap<PublicKey, AtomicIsize>>,
pending: RwLock<HashMap<Signature, Plan>>,
@ -64,16 +42,16 @@ pub struct Bank {
impl Bank {
/// Create an Bank using a deposit.
pub fn new_from_deposit(deposit: &Payment) -> Self {
let balances = RwLock::new(HashMap::new());
apply_payment(&balances, deposit);
Bank {
balances,
let bank = Bank {
balances: RwLock::new(HashMap::new()),
pending: RwLock::new(HashMap::new()),
last_ids: RwLock::new(VecDeque::new()),
time_sources: RwLock::new(HashSet::new()),
last_time: RwLock::new(Utc.timestamp(0, 0)),
transaction_count: AtomicUsize::new(0),
}
};
bank.apply_payment(deposit);
bank
}
/// Create an Bank with only a Mint. Typically used by unit tests.
@ -87,6 +65,28 @@ impl Bank {
bank
}
/// Commit funds to the 'to' party.
fn apply_payment(&self, payment: &Payment) {
// First we check balances with a read lock to maximize potential parallelization.
if self.balances
.read()
.expect("'balances' read lock in apply_payment")
.contains_key(&payment.to)
{
let bals = self.balances.read().expect("'balances' read lock");
bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
} else {
// 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.
let mut bals = self.balances.write().expect("'balances' write lock");
if bals.contains_key(&payment.to) {
bals[&payment.to].fetch_add(payment.tokens as isize, Ordering::Relaxed);
} else {
bals.insert(payment.to, AtomicIsize::new(payment.tokens as isize));
}
}
}
/// Return the last entry ID registered
pub fn last_id(&self) -> Hash {
let last_ids = self.last_ids.read().expect("'last_ids' read lock");
@ -214,7 +214,7 @@ impl Bank {
.expect("timestamp creation in apply_credits")));
if let Some(ref payment) = plan.final_payment() {
apply_payment(&self.balances, payment);
self.apply_payment(payment);
} else {
let mut pending = self.pending
.write()
@ -277,7 +277,7 @@ impl Bank {
{
e.get_mut().apply_witness(&Witness::Signature(from));
if let Some(payment) = e.get().final_payment() {
apply_payment(&self.balances, &payment);
self.apply_payment(&payment);
e.remove_entry();
}
};
@ -325,7 +325,7 @@ impl Bank {
.read()
.expect("'last_time' read lock when creating timestamp")));
if let Some(ref payment) = plan.final_payment() {
apply_payment(&self.balances, payment);
self.apply_payment(payment);
completed.push(key.clone());
}
}