Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 28 additions & 20 deletions src/day1/CoarseGrainedBank.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package day1

import day1.Bank.Companion.MAX_AMOUNT
import java.util.concurrent.locks.*
import kotlin.concurrent.withLock

class CoarseGrainedBank(accountsNumber: Int) : Bank {
private val accounts: Array<Account> = Array(accountsNumber) { Account() }
Expand All @@ -13,37 +14,44 @@ class CoarseGrainedBank(accountsNumber: Int) : Bank {

override fun getAmount(id: Int): Long {
// TODO: Make this operation thread-safe via coarse-grained locking.
return accounts[id].amount
return globalLock.withLock {
accounts[id].amount
}
}

override fun deposit(id: Int, amount: Long): Long {
// TODO: Make this operation thread-safe via coarse-grained locking.
require(amount > 0) { "Invalid amount: $amount" }
val account = accounts[id]
check(!(amount > MAX_AMOUNT || account.amount + amount > MAX_AMOUNT)) { "Overflow" }
account.amount += amount
return account.amount
return globalLock.withLock {
require(amount > 0) { "Invalid amount: $amount" }
val account = accounts[id]
check(!(amount > MAX_AMOUNT || account.amount + amount > MAX_AMOUNT)) { "Overflow" }
account.amount += amount
account.amount
}
}

override fun withdraw(id: Int, amount: Long): Long {
// TODO: Make this operation thread-safe via coarse-grained locking.
require(amount > 0) { "Invalid amount: $amount" }
val account = accounts[id]
check(account.amount - amount >= 0) { "Underflow" }
account.amount -= amount
return account.amount
return globalLock.withLock {
require(amount > 0) { "Invalid amount: $amount" }
val account = accounts[id]
check(account.amount - amount >= 0) { "Underflow" }
account.amount -= amount
account.amount
}
}

override fun transfer(fromId: Int, toId: Int, amount: Long) {
// TODO: Make this operation thread-safe via coarse-grained locking.
require(amount > 0) { "Invalid amount: $amount" }
require(fromId != toId) { "fromIndex == toIndex" }
val from = accounts[fromId]
val to = accounts[toId]
check(amount <= from.amount) { "Underflow" }
check(!(amount > MAX_AMOUNT || to.amount + amount > MAX_AMOUNT)) { "Overflow" }
from.amount -= amount
to.amount += amount
globalLock.withLock {
require(amount > 0) { "Invalid amount: $amount" }
require(fromId != toId) { "fromIndex == toIndex" }
val from = accounts[fromId]
val to = accounts[toId]
check(amount <= from.amount) { "Underflow" }
check(!(amount > MAX_AMOUNT || to.amount + amount > MAX_AMOUNT)) { "Overflow" }
from.amount -= amount
to.amount += amount
}
}

/**
Expand Down
42 changes: 29 additions & 13 deletions src/day1/FineGrainedBank.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,60 @@ package day1

import day1.Bank.Companion.MAX_AMOUNT
import java.util.concurrent.locks.*
import kotlin.concurrent.withLock

class FineGrainedBank(accountsNumber: Int) : Bank {
private val accounts: Array<Account> = Array(accountsNumber) { Account() }

override fun getAmount(id: Int): Long {
// TODO: Make this operation thread-safe via fine-grained locking.
val account = accounts[id]
return account.amount
return account.lock.withLock {
account.amount
}
}

override fun deposit(id: Int, amount: Long): Long {
// TODO: Make this operation thread-safe via fine-grained locking.
require(amount > 0) { "Invalid amount: $amount" }
val account = accounts[id]
check(!(amount > MAX_AMOUNT || account.amount + amount > MAX_AMOUNT)) { "Overflow" }
account.amount += amount
return account.amount
return account.lock.withLock {
check(!(amount > MAX_AMOUNT || account.amount + amount > MAX_AMOUNT)) { "Overflow" }
account.amount += amount
account.amount
}
}

override fun withdraw(id: Int, amount: Long): Long {
// TODO: Make this operation thread-safe via fine-grained locking.
require(amount > 0) { "Invalid amount: $amount" }
val account = accounts[id]
check(account.amount - amount >= 0) { "Underflow" }
account.amount -= amount
return account.amount
return account.lock.withLock {
check(account.amount - amount >= 0) { "Underflow" }
account.amount -= amount
account.amount
}
}

override fun transfer(fromId: Int, toId: Int, amount: Long) {
// TODO: Make this operation thread-safe via fine-grained locking.
require(amount > 0) { "Invalid amount: $amount" }
require(fromId != toId) { "fromId == toId" }
val from = accounts[fromId]
val to = accounts[toId]
check(amount <= from.amount) { "Underflow" }
check(!(amount > MAX_AMOUNT || to.amount + amount > MAX_AMOUNT)) { "Overflow" }
from.amount -= amount
to.amount += amount

val minId = minOf(fromId, toId)
val maxId = maxOf(fromId, toId)
val minAccount = accounts[minId]
val maxAccount = accounts[maxId]
minAccount.lock.withLock {
maxAccount.lock.withLock {
val from = accounts[fromId]
val to = accounts[toId]
check(amount <= from.amount) { "Underflow" }
check(!(amount > MAX_AMOUNT || to.amount + amount > MAX_AMOUNT)) { "Overflow" }
from.amount -= amount
to.amount += amount
}
}
}

/**
Expand Down
15 changes: 9 additions & 6 deletions src/day1/TreiberStack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@ class TreiberStack<E> : Stack<E> {
// TODO: Make me linearizable!
// TODO: Update `top` via Compare-and-Set,
// TODO: restarting the operation on CAS failure.
val curTop = top.get()
val newTop = Node(element, curTop)
top.set(newTop)
while (true) {
val curTop = top.get()
val newTop = Node(element, curTop)
if (top.compareAndSet(curTop, newTop)) return
}
}

override fun pop(): E? {
// TODO: Make me linearizable!
// TODO: Update `top` via Compare-and-Set,
// TODO: restarting the operation on CAS failure.
val curTop = top.get() ?: return null
top.set(curTop.next)
return curTop.element
while (true) {
val curTop = top.get() ?: return null
if (top.compareAndSet(curTop, curTop.next)) return curTop.element
}
}

private class Node<E>(
Expand Down
41 changes: 24 additions & 17 deletions src/day1/TreiberStackWithElimination.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package day1

import java.util.concurrent.*
import java.util.concurrent.atomic.*
import java.util.concurrent.ThreadLocalRandom
import java.util.concurrent.atomic.AtomicReferenceArray

open class TreiberStackWithElimination<E> : Stack<E> {
private val stack = TreiberStack<E>()

// TODO: Try to optimize concurrent push and pop operations,
// TODO: synchronizing them in an `eliminationArray` cell.
private val eliminationArray = AtomicReferenceArray<Any?>(ELIMINATION_ARRAY_SIZE)

override fun push(element: E) {
Expand All @@ -16,24 +14,33 @@ open class TreiberStackWithElimination<E> : Stack<E> {
}

protected open fun tryPushElimination(element: E): Boolean {
TODO("Implement me!")
// TODO: Choose a random cell in `eliminationArray`
// TODO: and try to install the element there.
// TODO: Wait `ELIMINATION_WAIT_CYCLES` loop cycles
// TODO: in hope that a concurrent `pop()` grabs the
// TODO: element. If so, clean the cell and finish,
// TODO: returning `true`. Otherwise, move the cell
// TODO: to the empty state and return `false`.


val randomCellIdx = randomCellIndex()
if (!eliminationArray.compareAndSet(randomCellIdx, CELL_STATE_EMPTY, element)) return false
repeat(ELIMINATION_WAIT_CYCLES) {
if (eliminationArray.compareAndSet(randomCellIdx, CELL_STATE_RETRIEVED, CELL_STATE_EMPTY)) return true
}
if (eliminationArray.compareAndSet(randomCellIdx, element, CELL_STATE_EMPTY)) return false
if (eliminationArray.compareAndSet(randomCellIdx, CELL_STATE_RETRIEVED, CELL_STATE_EMPTY)) return true

throw IllegalStateException("The cell state is in an unexpected state")

}

override fun pop(): E? = tryPopElimination() ?: stack.pop()

private fun tryPopElimination(): E? {
TODO("Implement me!")
// TODO: Choose a random cell in `eliminationArray`
// TODO: and try to retrieve an element from there.
// TODO: On success, return the element.
// TODO: Otherwise, if the cell is empty, return `null`.


val randomCellIdx = randomCellIndex()
val element = eliminationArray.get(randomCellIdx)
if (element == CELL_STATE_EMPTY || element == CELL_STATE_RETRIEVED) return null
if (eliminationArray.compareAndSet(randomCellIdx, element, CELL_STATE_RETRIEVED)) {
return element as E?
}
return null

}

private fun randomCellIndex(): Int =
Expand Down