1+ package cub.concurrent.tasks
2+
3+ import kotlin.concurrent.thread
4+
5+ /*
6+ * Implement TransactionalBankAccount class.
7+ *
8+ * It should implement BankAccount interface, and, in addition to that, also provide `transaction` function.
9+ * This function should allow to perform a series of actions on a bank account atomically in one transaction.
10+ *
11+ * Consider the following example:
12+ *
13+ * val account = TransactionalBankAccount()
14+ * val t1 = thread {
15+ * account.transaction {
16+ * withdraw(1_000)
17+ * withdraw(1_000)
18+ * }
19+ * }
20+ * val t2 = thread {
21+ * account.deposit(1_500)
22+ * }
23+ * t1.join(); t2.join()
24+ * println(account.balance)
25+ *
26+ * In this program, the only possible outcome should be 1_500,
27+ * because under any interleaving of threads it is not possible
28+ * to withdraw 1_000 + 1_000 = 2_000 in a single transaction.
29+ *
30+ * Compare with the following example:
31+ *
32+ * val account = TransactionalBankAccount()
33+ * val t1 = thread {
34+ * account.withdraw(1_000)
35+ * account.withdraw(1_000)
36+ * }
37+ * val t2 = thread {
38+ * account.deposit(1_500)
39+ * }
40+ * t1.join(); t2.join()
41+ * println(account.balance)
42+ *
43+ * Here the result can be either 1_500 or 500.
44+ */
45+
46+ class TransactionalBankAccount : BankAccount {
47+
48+ override val balance: Int
49+ get() = TODO ()
50+
51+ fun <T > transaction (block : BankAccount .() -> T ): T {
52+ TODO ()
53+ }
54+
55+ override fun deposit (amount : Int ) {
56+ transaction { deposit(amount) }
57+ }
58+
59+ override fun withdraw (amount : Int ): Boolean {
60+ return transaction { withdraw(amount) }
61+ }
62+
63+ }
64+
65+ fun transactionalBankAccountTest1 () {
66+ val account = TransactionalBankAccount ()
67+ val t1 = thread {
68+ account.transaction {
69+ withdraw(1_000 )
70+ withdraw(1_000 )
71+ }
72+ }
73+ val t2 = thread {
74+ account.deposit(1_500 )
75+ }
76+ t1.join(); t2.join()
77+ check(account.balance == 1_500 )
78+ }
79+
80+ fun transactionalBankAccountTest2 () {
81+ val account = TransactionalBankAccount ()
82+ val t1 = thread {
83+ account.withdraw(1_000 )
84+ account.withdraw(1_000 )
85+ }
86+ val t2 = thread {
87+ account.deposit(1_500 )
88+ }
89+ t1.join(); t2.join()
90+ check(account.balance == 1_500 || account.balance == 500 )
91+ }
92+
93+ fun TransactionalBankAccount.buyWithCashback (price : Int , cashbackPercentage : Int ): Boolean = this .transaction {
94+ val cashback = (price * cashbackPercentage + 50 ) / 100
95+ if (withdraw(price)) {
96+ deposit(cashback)
97+ true
98+ } else false
99+ }
100+
101+ fun transactionalBankAccountTest3 () {
102+ val account = TransactionalBankAccount ()
103+ account.deposit(10_000 )
104+ val t1 = thread {
105+ account.buyWithCashback(1_000 , 20 )
106+ }
107+ val t2 = thread {
108+ account.buyWithCashback(5_000 , 5 )
109+ }
110+ val t3 = thread {
111+ account.buyWithCashback(10_000 , 5 )
112+ }
113+ t1.join(); t2.join(); t3.join()
114+ check(account.balance == 4_700 )
115+ }
116+
117+ fun main () {
118+ transactionalBankAccountTest1()
119+ transactionalBankAccountTest2()
120+ transactionalBankAccountTest3()
121+ }
0 commit comments