Last active
May 28, 2021 19:02
-
-
Save Charlyzzz/605a4052142d3ff85b618235c2b5acea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import kotlinx.coroutines.CoroutineScope | |
import kotlinx.coroutines.channels.Channel | |
import kotlinx.coroutines.channels.SendChannel | |
import kotlinx.coroutines.channels.consumeEach | |
import kotlinx.coroutines.delay | |
import kotlinx.coroutines.launch | |
import kotlinx.coroutines.runBlocking | |
import java.util.* | |
fun main() { | |
runBlocking { | |
table(5) | |
} | |
} | |
sealed interface TableCommand | |
data class WantChopsticks(val philosopher: Int) : TableCommand | |
data class DoneEating(val philosopher: Int) : TableCommand | |
fun CoroutineScope.table(nPhilosophers: Int) = launch { | |
val table = Channel<TableCommand>(nPhilosophers) | |
val seed = mutableListOf<Philosopher>() | |
val philosophers = (0 until nPhilosophers).fold(seed) { philosophers, i -> | |
philosophers.add(philosopher(i, table)) | |
philosophers | |
} | |
philosophers.forEachIndexed { philosopher, _ -> table.send(WantChopsticks(philosopher)) } | |
val chopsticks = Array(nPhilosophers) { true } | |
val stats = stats(nPhilosophers) | |
val waiting: Queue<WantChopsticks> = LinkedList() | |
table.consumeEach { | |
when (it) { | |
is WantChopsticks -> { | |
val acquired = tryAcquire(it.philosopher, chopsticks) | |
if (acquired) { | |
philosophers[it.philosopher].send(ChopsticksReady) | |
} else { | |
waiting.add(it) | |
} | |
} | |
is DoneEating -> { | |
release(it.philosopher, chopsticks) | |
stats.send(Track(it.philosopher)) | |
waiting.add(WantChopsticks(it.philosopher)) | |
val p = waiting.poll() | |
table.send(p) | |
} | |
} | |
} | |
} | |
fun release(philosopher: Int, chopsticks: Array<Boolean>) { | |
val leftChopstick = leftChopstickIndex(philosopher) | |
val rightChopstick = rightChopstickIndex(philosopher, chopsticks) | |
chopsticks[leftChopstick] = true | |
chopsticks[rightChopstick] = true | |
} | |
fun tryAcquire(philosopher: Int, chopsticks: Array<Boolean>): Boolean { | |
val leftChopstick = leftChopstickIndex(philosopher) | |
val rightChopstick = rightChopstickIndex(philosopher, chopsticks) | |
val chopsticksAvailable = chopsticks[leftChopstick] && chopsticks[rightChopstick] | |
if (chopsticksAvailable) { | |
chopsticks[leftChopstick] = false | |
chopsticks[rightChopstick] = false | |
} | |
return chopsticksAvailable | |
} | |
object ChopsticksReady | |
typealias Philosopher = SendChannel<ChopsticksReady> | |
fun CoroutineScope.philosopher(id: Int, table: SendChannel<DoneEating>): Philosopher { | |
val doneEating = DoneEating(id) | |
val philosopher = Channel<ChopsticksReady>() | |
launch { | |
philosopher.consumeEach { | |
delay(10) | |
table.send(doneEating) | |
} | |
} | |
return philosopher | |
} | |
private fun leftChopstickIndex(philosopher: Int) = philosopher | |
private fun rightChopstickIndex(philosopher: Int, chopsticks: Array<Boolean>): Int { | |
return if (philosopher == chopsticks.size - 1) { | |
0 | |
} else { | |
philosopher + 1 | |
} | |
} | |
sealed interface StatsCommands | |
object PrintTimer : StatsCommands | |
data class Track(val philosopher: Int) : StatsCommands | |
fun CoroutineScope.stats(nPhilosophers: Int): SendChannel<Track> { | |
val stats = Array(nPhilosophers) { 0 } | |
val statsChannel = Channel<StatsCommands>() | |
launch { | |
while (true) { | |
delay(1000) | |
statsChannel.send(PrintTimer) | |
} | |
} | |
launch { | |
statsChannel.consumeEach { | |
when (it) { | |
is PrintTimer -> | |
println(stats.joinToString(", ")) | |
is Track -> | |
stats[it.philosopher] += 1 | |
} | |
} | |
} | |
return statsChannel | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment