scala project: init
This commit is contained in:
1
scala_project_2025/.gitignore
vendored
Normal file
1
scala_project_2025/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Learn Concurrent Programming in Scala.pdf
|
||||
46
scala_project_2025/ConcurrencyTroubles.scala
Normal file
46
scala_project_2025/ConcurrencyTroubles.scala
Normal file
@@ -0,0 +1,46 @@
|
||||
package example
|
||||
|
||||
object ConcurrencyTroubles {
|
||||
private var value1: Int = 1000
|
||||
private var value2: Int = 0
|
||||
private var sum: Int = 0
|
||||
|
||||
def moveOneUnit(): Unit = {
|
||||
value1 -= 1
|
||||
value2 += 1
|
||||
if(value1 == 0) {
|
||||
value1 = 1000
|
||||
value2 = 0
|
||||
}
|
||||
}
|
||||
|
||||
def updateSum(): Unit = {
|
||||
sum = value1 + value2
|
||||
}
|
||||
|
||||
def execute(): Unit = {
|
||||
while(true) {
|
||||
moveOneUnit()
|
||||
updateSum()
|
||||
Thread.sleep(50)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the "main" method, the entry point of execution.
|
||||
// It could have been placed in a different file.
|
||||
def main(args: Array[String]): Unit = {
|
||||
for (i <- 1 to 2) {
|
||||
val thread = new Thread {
|
||||
override def run = execute()
|
||||
}
|
||||
thread.start()
|
||||
}
|
||||
|
||||
while(true) {
|
||||
updateSum()
|
||||
println(sum + " [" + value1 + " " + value2 + "]")
|
||||
Thread.sleep(100)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BIN
scala_project_2025/Project_2025.pdf
Normal file
BIN
scala_project_2025/Project_2025.pdf
Normal file
Binary file not shown.
6
scala_project_2025/bank_system/build.sbt
Normal file
6
scala_project_2025/bank_system/build.sbt
Normal file
@@ -0,0 +1,6 @@
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.0" % "test"
|
||||
|
||||
resolvers += "Central" at "https://central.maven.org/maven2/"
|
||||
|
||||
scalacOptions := Seq("-unchecked", "-deprecation", "-feature", "-language:postfixOps")
|
||||
|
||||
11
scala_project_2025/bank_system/src/main/scala/Account.scala
Normal file
11
scala_project_2025/bank_system/src/main/scala/Account.scala
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
class Account(val code : String, val balance: Double) {
|
||||
|
||||
// TODO
|
||||
// Implement functions. Account should be immutable.
|
||||
// Change return type to the appropriate one
|
||||
def withdraw(amount: Double) : Unit = ???
|
||||
|
||||
def deposit (amount: Double) : Unit = ???
|
||||
|
||||
}
|
||||
65
scala_project_2025/bank_system/src/main/scala/Bank.scala
Normal file
65
scala_project_2025/bank_system/src/main/scala/Bank.scala
Normal file
@@ -0,0 +1,65 @@
|
||||
import collection.mutable.Map
|
||||
|
||||
class Bank(val allowedAttempts: Integer = 3) {
|
||||
|
||||
private val accountsRegistry : Map[String,Account] = Map()
|
||||
|
||||
val transactionsPool: TransactionPool = new TransactionPool()
|
||||
val completedTransactions: TransactionPool = new TransactionPool()
|
||||
|
||||
|
||||
def processing : Boolean = !transactionsPool.isEmpty
|
||||
|
||||
// TODO
|
||||
// Adds a new transaction for the transfer to the transaction pool
|
||||
def transfer(from: String, to: String, amount: Double): Unit = ???
|
||||
|
||||
// TODO
|
||||
// Process the transactions in the transaction pool
|
||||
// The implementation needs to be completed and possibly fixed
|
||||
def processTransactions: Unit = {
|
||||
|
||||
// val workers : List[Thread] = transactionsPool.iterator.toList
|
||||
// .filter(/* select only pending transactions */)
|
||||
// .map(processSingleTransaction)
|
||||
|
||||
// workers.map( element => element.start() )
|
||||
// workers.map( element => element.join() )
|
||||
|
||||
/* TODO: change to select only transactions that succeeded */
|
||||
// val succeded : List[Transaction] = transactionsPool
|
||||
|
||||
/* TODO: change to select only transactions that failed */
|
||||
// val failed : List[Transaction] = transactionsPool
|
||||
|
||||
// succeded.map(/* remove transactions from the transaction pool */)
|
||||
// succeded.map(/* add transactions to the completed transactions queue */)
|
||||
|
||||
//failed.map(t => {
|
||||
/* transactions that failed need to be set as pending again;
|
||||
if the number of retry has exceeded they also need to be removed from
|
||||
the transaction pool and to be added to the queue of completed transactions */
|
||||
//})
|
||||
|
||||
if(!transactionsPool.isEmpty) {
|
||||
processTransactions
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
// The function creates a new thread ready to process
|
||||
// the transaction, and returns it as a return value
|
||||
private def processSingleTransaction(t : Transaction) : Thread = ???
|
||||
|
||||
|
||||
// TODO
|
||||
// Creates a new account and returns its code to the user.
|
||||
// The account is stored in the local registry of bank accounts.
|
||||
def createAccount(initialBalance: Double) : String = ???
|
||||
|
||||
|
||||
// TODO
|
||||
// Return information about a certain account based on its code.
|
||||
// Remember to handle the case in which the account does not exist
|
||||
def getAccount(code : String) : Option[Account] = ???
|
||||
}
|
||||
13
scala_project_2025/bank_system/src/main/scala/Main.scala
Normal file
13
scala_project_2025/bank_system/src/main/scala/Main.scala
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
object Main extends App {
|
||||
|
||||
def thread(body: => Unit): Thread = {
|
||||
val t = new Thread {
|
||||
override def run() = body
|
||||
}
|
||||
|
||||
t.start
|
||||
t
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
object TransactionStatus extends Enumeration {
|
||||
val SUCCESS, PENDING, FAILED = Value
|
||||
}
|
||||
|
||||
class TransactionPool {
|
||||
|
||||
// Remove and the transaction from the pool
|
||||
def remove(t: Transaction): Boolean = ???
|
||||
|
||||
// Return whether the queue is empty
|
||||
def isEmpty: Boolean = ???
|
||||
|
||||
// Return the size of the pool
|
||||
def size: Integer = ???
|
||||
|
||||
// Add new element to the back of the queue
|
||||
def add(t: Transaction): Boolean = ???
|
||||
|
||||
// Return an iterator to allow you to iterate over the queue
|
||||
def iterator : Iterator[Transaction] = ???
|
||||
|
||||
}
|
||||
|
||||
class Transaction(val from: String,
|
||||
val to: String,
|
||||
val amount: Double,
|
||||
val retries: Int = 3) {
|
||||
|
||||
private var status: TransactionStatus.Value = TransactionStatus.PENDING
|
||||
private var attempts = 0
|
||||
|
||||
def getStatus() = status
|
||||
|
||||
// TODO: Implement methods that change the status of the transaction
|
||||
|
||||
}
|
||||
206
scala_project_2025/bank_system/src/test/scala/AccountTests.scala
Normal file
206
scala_project_2025/bank_system/src/test/scala/AccountTests.scala
Normal file
@@ -0,0 +1,206 @@
|
||||
import org.scalatest.FunSuite
|
||||
|
||||
class AccountTests extends FunSuite {
|
||||
|
||||
test("Test 01: Account should be immutable") {
|
||||
val account = new Account("1234", 500)
|
||||
val result = account.withdraw(200)
|
||||
assert(account.balance == 500)
|
||||
assert(account.code == "1234")
|
||||
}
|
||||
|
||||
test("Test 02: Valid account withdrawal") {
|
||||
val account = new Account("1234", 500)
|
||||
val result = account.withdraw(200)
|
||||
result match {
|
||||
case Right(x) => assert(x.balance == 300)
|
||||
case Left(x) => assert(false)
|
||||
}
|
||||
}
|
||||
|
||||
test("Test 03: Invalid account withdrawal should return error message") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.withdraw(1000)
|
||||
assert(result.isLeft)
|
||||
}
|
||||
|
||||
test("Test 04: Withdrawal of negative amount should return error message") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.withdraw(-100)
|
||||
assert(result.isLeft)
|
||||
}
|
||||
|
||||
test("Test 05: Valid account deposit") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.deposit(250)
|
||||
assert(result.isRight)
|
||||
assert(result.toOption.get.balance == 750)
|
||||
}
|
||||
|
||||
test("Test 06: Deposit of negative amount should return an error") {
|
||||
val account = new Account("1234",500)
|
||||
val result = account.deposit(-50)
|
||||
assert(result.isLeft)
|
||||
}
|
||||
|
||||
test("Test 07: Correct balance amount after several withdrawals and deposits") {
|
||||
var account = new Account("1234",50000)
|
||||
val lock: Object = new Object
|
||||
|
||||
val first = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.withdraw(10).toOption.get
|
||||
}
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
val second = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.deposit(5).toOption.get
|
||||
}
|
||||
Thread.sleep(20)
|
||||
}
|
||||
}
|
||||
val third = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.withdraw(50).toOption.get
|
||||
}
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
val fourth = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
lock.synchronized {
|
||||
account = account.deposit(100).toOption.get
|
||||
}
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
first.join()
|
||||
second.join()
|
||||
third.join()
|
||||
fourth.join()
|
||||
assert(account.balance == 54500)
|
||||
}
|
||||
}
|
||||
|
||||
class AccountTransferTests extends FunSuite {
|
||||
|
||||
test("Test 08: Valid transfer between accounts") {
|
||||
val bank = new Bank()
|
||||
|
||||
val code1 = bank.createAccount(100)
|
||||
val code2 = bank.createAccount(200)
|
||||
|
||||
bank transfer(code1, code2, 50)
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.iterator.toList.last.getStatus == TransactionStatus.SUCCESS)
|
||||
assert(bank.getAccount(code1).get.balance == 50)
|
||||
assert(bank.getAccount(code2).get.balance == 250)
|
||||
}
|
||||
|
||||
test("Test 09: Transfer of negative amount between accounts should fail") {
|
||||
val bank = new Bank()
|
||||
|
||||
val code1 = bank.createAccount(500)
|
||||
val code2 = bank.createAccount(1000)
|
||||
|
||||
bank transfer(code1, code2, -100)
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.iterator.toList.last.getStatus == TransactionStatus.FAILED)
|
||||
assert(bank.getAccount(code1).get.balance == 500)
|
||||
assert(bank.getAccount(code2).get.balance == 1000)
|
||||
}
|
||||
|
||||
|
||||
test("Test 10: Invalid transfer between accounts due to insufficient funds should lead to transaction status FAILED and no money should be transferred between accounts") {
|
||||
val bank = new Bank()
|
||||
val code1 = bank.createAccount(100)
|
||||
val code2 = bank.createAccount(1000)
|
||||
|
||||
bank transfer(code1, code2, 150)
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.iterator.toList.last.getStatus == TransactionStatus.FAILED)
|
||||
assert(bank.getAccount(code1).get.balance == 100)
|
||||
assert(bank.getAccount(code2).get.balance == 1000)
|
||||
}
|
||||
|
||||
|
||||
test("Test 11: Correct balance amounts after several transfers") {
|
||||
val bank = new Bank()
|
||||
val code1 = bank.createAccount(3000)
|
||||
val code2 = bank.createAccount(5000)
|
||||
|
||||
val first = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code1, code2, 30)
|
||||
}
|
||||
}
|
||||
val second = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code2, code1, 23)
|
||||
}
|
||||
}
|
||||
first.join()
|
||||
second.join()
|
||||
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.getAccount(code1).get.balance == 2300)
|
||||
assert(bank.getAccount(code2).get.balance == 5700)
|
||||
}
|
||||
|
||||
test("Test 12: All the submitted transactions are processed") {
|
||||
val bank = new Bank()
|
||||
val code1 = bank.createAccount(3000)
|
||||
val code2 = bank.createAccount(5000)
|
||||
|
||||
val first = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code1, code2, 30)
|
||||
}
|
||||
}
|
||||
val second = Main.thread {
|
||||
for (i <- 0 until 100) {
|
||||
bank transfer(code2, code1, 23)
|
||||
}
|
||||
}
|
||||
first.join()
|
||||
second.join()
|
||||
|
||||
val submitted = bank.transactionsPool.size
|
||||
|
||||
bank processTransactions
|
||||
|
||||
while (bank.processing) {
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
assert(bank.completedTransactions.size == submitted)
|
||||
assert(bank.transactionsPool.isEmpty)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user