Skip to content

Commit f3f72f7

Browse files
committed
Fixed a missed lock in safe bank global-style.
1 parent b9a7c04 commit f3f72f7

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import datetime
2+
import random
3+
import time
4+
from threading import Thread, RLock
5+
from typing import List
6+
7+
8+
class Account:
9+
def __init__(self, balance=0):
10+
self.balance = balance
11+
12+
13+
def main():
14+
accounts = create_accounts()
15+
total = sum(a.balance for a in accounts)
16+
17+
validate_bank(accounts, total)
18+
print("Starting transfers...")
19+
20+
jobs = [
21+
Thread(target=do_bank_stuff, args=(accounts, total)),
22+
Thread(target=do_bank_stuff, args=(accounts, total)),
23+
Thread(target=do_bank_stuff, args=(accounts, total)),
24+
Thread(target=do_bank_stuff, args=(accounts, total)),
25+
Thread(target=do_bank_stuff, args=(accounts, total)),
26+
]
27+
28+
t0 = datetime.datetime.now()
29+
30+
[j.start() for j in jobs]
31+
[j.join() for j in jobs]
32+
33+
dt = datetime.datetime.now() - t0
34+
35+
print("Transfers complete ({:,.2f}) sec".format(dt.total_seconds()))
36+
validate_bank(accounts, total)
37+
38+
39+
def do_bank_stuff(accounts, total):
40+
for _ in range(1, 10_000):
41+
a1, a2 = get_two_accounts(accounts)
42+
amount = random.randint(1, 100)
43+
do_transfer(a1, a2, amount)
44+
validate_bank(accounts, total, quiet=True)
45+
46+
47+
def create_accounts() -> List[Account]:
48+
return [
49+
Account(balance=5000),
50+
Account(balance=10000),
51+
Account(balance=7500),
52+
Account(balance=7000),
53+
Account(balance=6000),
54+
Account(balance=9000),
55+
]
56+
57+
58+
transfer_lock = RLock()
59+
60+
61+
def do_transfer(from_account: Account, to_account: Account, amount: int):
62+
if from_account.balance < amount:
63+
return
64+
65+
# Not so good:
66+
# transfer_lock.acquire()
67+
#
68+
# from_account.balance -= amount
69+
# time.sleep(.000)
70+
# to_account.balance += amount
71+
#
72+
# transfer_lock.release()
73+
74+
# good!
75+
with transfer_lock:
76+
from_account.balance -= amount
77+
time.sleep(.000)
78+
to_account.balance += amount
79+
80+
81+
def validate_bank(accounts: List[Account], total: int, quiet=False):
82+
with transfer_lock:
83+
current = sum(a.balance for a in accounts)
84+
85+
if current != total:
86+
print("ERROR: Inconsistent account balance: ${:,} vs ${:,}".format(
87+
current, total
88+
), flush=True)
89+
elif not quiet:
90+
print("All good: Consistent account balance: ${:,}".format(
91+
total), flush=True)
92+
93+
94+
def get_two_accounts(accounts):
95+
a1 = random.choice(accounts)
96+
a2 = a1
97+
while a2 == a1:
98+
a2 = random.choice(accounts)
99+
100+
return a1, a2
101+
102+
103+
if __name__ == '__main__':
104+
main()

0 commit comments

Comments
 (0)