Skip to content

Instantly share code, notes, and snippets.

@fancellu
Created February 16, 2024 14:23
Show Gist options
  • Save fancellu/b0b6d63d479ed9dc56b4d9547d7d8dc3 to your computer and use it in GitHub Desktop.
Save fancellu/b0b6d63d479ed9dc56b4d9547d7d8dc3 to your computer and use it in GitHub Desktop.
Dining Philosophers with Cats Effect
import cats.effect.std.Semaphore
import cats.effect._
import cats.implicits._
import scala.concurrent.duration._
object DiningPhilosophersIOApp extends IOApp {
case class Fork(id: Int, lock: Semaphore[IO])
case class Philosopher(name: String, left: Fork, right: Fork)
val random = new scala.util.Random(System.currentTimeMillis())
def run(args: List[String]): IO[ExitCode] = {
val FORK_COUNT = 7
val forks = (0 until FORK_COUNT).map { id =>
Semaphore[IO](1).map(id -> Fork(id, _))
}.toList.sequence.map(_.toMap)
for {
forkMap <- forks
// create philosophers, give them forks
philosophers: Seq[Philosopher] = (0 until FORK_COUNT).map { id =>
val left = forkMap(id)
val right = forkMap((id + 1) % FORK_COUNT)
Philosopher(s"Philosopher $id", left, right)
}.toList
// spin them up in parallel
fibres <- philosophers.traverse(eat(_).start)
// wait for them to finish
_ <- fibres.toList.map(_.joinWithUnit).sequence
} yield ExitCode.Success
}
def eat(philosopher: Philosopher): IO[Unit] = for {
left_id <- philosopher.left.id.pure[IO]
right_id <- philosopher.right.id.pure[IO]
(firstFork: Fork, secondFork: Fork) <- IO {
if (left_id < right_id)
(philosopher.left, philosopher.right)
else
(philosopher.right, philosopher.left)
}
_ <- firstFork.lock.acquire
_ <- secondFork.lock.acquire
_ <- IO.println(s"${philosopher.name} picked up fork $left_id and $right_id")
random_delay <- IO(400 + random.nextLong(400))
_ <- IO.println(s"${philosopher.name} is sleeping for $random_delay")
_ <- IO.sleep(random_delay.millis)
_ <- IO.println(s"${philosopher.name} is done eating")
_ <- secondFork.lock.release
_ <- firstFork.lock.release
} yield ()
}
@fancellu
Copy link
Author

Output

Philosopher 2 picked up fork 2 and 3
Philosopher 5 picked up fork 5 and 6
Philosopher 0 picked up fork 0 and 1
Philosopher 5 is sleeping for 717
Philosopher 2 is sleeping for 536
Philosopher 0 is sleeping for 708
Philosopher 2 is done eating
Philosopher 0 is done eating
Philosopher 1 picked up fork 1 and 2
Philosopher 1 is sleeping for 623
Philosopher 5 is done eating
Philosopher 6 picked up fork 6 and 0
Philosopher 6 is sleeping for 515
Philosopher 4 picked up fork 4 and 5
Philosopher 4 is sleeping for 514
Philosopher 6 is done eating
Philosopher 4 is done eating
Philosopher 3 picked up fork 3 and 4
Philosopher 3 is sleeping for 576
Philosopher 1 is done eating
Philosopher 3 is done eating

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment