import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock class Fork(id: String) : Observable(id) { //TODO // --------------- EDIT CODE BELOW --------------- val lock = Mutex() suspend fun take(philosopher: String) = lock.withLock { setOccupiedBy(philosopher) } suspend fun release() { setOccupiedBy(null) } // --------------- EDIT CODE ABOVE --------------- private suspend fun setOccupiedBy(philosopher: String?) { delay(5L) onChange(philosopher) occupiedBy = philosopher } private var occupiedBy: String? = null fun available() = occupiedBy == null } class Philosopher( id: String, private val leftFork: Fork, private val rightFork: Fork, ) : Observable(id) { //TODO // --------------- EDIT CODE BELOW --------------- suspend fun start() = repeat(10) { think() takeForks() eat() releaseForks() }.also { changeState("FINISHED") } private suspend fun takeForks() { while (!forksavailable()) { wait() } leftFork.take(id) rightFork.take(id) } private fun forksavailable() : Boolean { return leftFork.available() && rightFork.available() } private suspend fun releaseForks() { leftFork.release() rightFork.release() } // --------------- EDIT CODE ABOVE --------------- private suspend fun wait() { delay(1) } private suspend fun eat() { changeState("EATING") delay((0L..10L).random()) } private suspend fun think() { changeState("THINKING") delay((0L..10L).random()) changeState("HUNGRY") } private suspend fun changeState(state: String) { onChange(state) this.state = state } private var state = "THINKING" } open class Observable(val id: String) { var onChange: suspend (String, T) -> Unit = { _, _ -> } protected suspend fun onChange(state: T) = onChange(id, state) } suspend fun main(args: Array) = runBlocking { val number = 5 val forks = Array(number) { Fork("Fork$it") } val philosophers = Array(number) { index -> Philosopher( id = "Philosopher$index", leftFork = forks[index], rightFork = forks[(index + 1) % number] ) } philosophers.forEach { it.onChange = { philosopher, state -> println("$philosopher is now $state") } } forks.forEach { it.onChange = { fork, philosopher -> println(philosopher?.let { "$fork acquired by $it" } ?: "$fork released") } } philosophers.forEach { launch { it.start() } } }