Skip to content

Instantly share code, notes, and snippets.

@mpilquist
Last active April 28, 2023 23:31
Show Gist options
  • Save mpilquist/011a2ce4f55791f73b9566d6ea56830d to your computer and use it in GitHub Desktop.
Save mpilquist/011a2ce4f55791f73b9566d6ea56830d to your computer and use it in GitHub Desktop.
Dining Philosophers with FS2
/*
scalaVersion := "2.12.7"
resolvers += Resolver.sonatypeRepo("snapshots")
libraryDependencies += "co.fs2" %% "fs2-core" % "1.0.1-SNAPSHOT"
*/
import cats._
import cats.implicits._
import cats.effect._
import cats.effect.concurrent.Semaphore
import cats.effect.implicits._
import fs2._
import scala.concurrent.duration._
import scala.util.Random
sealed trait Philosopher
case object Descartes extends Philosopher
case object Plato extends Philosopher
case object Socrates extends Philosopher
case object Aristotle extends Philosopher
case object Kant extends Philosopher
case class Fork(id: Int)
sealed trait Event
case class Acquired(p: Philosopher, f: Fork) extends Event
case class Ate(p: Philosopher) extends Event
case class Released(p: Philosopher, f: Fork) extends Event
object Philosophers extends IOApp {
def run(args: List[String]): IO[ExitCode] = {
def forksFor(p: Philosopher): (Fork, Fork) = p match {
case Descartes => (Fork(0), Fork(1))
case Plato => (Fork(1), Fork(2))
case Socrates => (Fork(2), Fork(3))
case Aristotle => (Fork(3), Fork(4))
case Kant => (Fork(0), Fork(4))
}
val forks: List[Fork] = List.tabulate(5)(Fork)
val forkSemaphores: IO[List[Semaphore[IO]]] = Semaphore[IO](1).replicateA(forks.size)
def acquire(forkSemaphores: List[Semaphore[IO]], p: Philosopher, f: Fork): IO[Event] =
forkSemaphores(f.id).acquire *> IO.pure(Acquired(p, f))
def release(forkSemaphores: List[Semaphore[IO]], p: Philosopher, f: Fork): IO[Event] =
forkSemaphores(f.id).release *> IO.pure(Released(p, f))
val randomSleep: Stream[IO, Nothing] = Stream.eval(IO((Random.nextInt % 1000).millis)).flatMap(Stream.sleep_(_))
def live(forkSemaphores: List[Semaphore[IO]], p: Philosopher): Stream[IO, Event] = {
val (first, second) = forksFor(p)
val once = Stream.eval(acquire(forkSemaphores, p, first)) ++
Stream.eval(acquire(forkSemaphores, p, second)) ++
randomSleep ++
Stream.emit(Ate(p)) ++
Stream.eval(release(forkSemaphores, p, second)) ++
Stream.eval(release(forkSemaphores, p, first)) ++
randomSleep
once.repeat
}
val events: Stream[IO, Event] = Stream.eval(forkSemaphores).flatMap { fs =>
Stream.emits(List(Descartes, Plato, Socrates, Aristotle, Kant).map(live(fs, _))).covary[IO].parJoinUnbounded
}
val showEvents = events.map(_.toString).to(Sink.showLinesStdOut)
showEvents.compile.drain.as(ExitCode.Success)
}
}
@oleg-py
Copy link

oleg-py commented Nov 16, 2018

I feel sudden desire to promote obscure functions from stdlib:
0.until(5).map(Fork(_)).toList ~ List.tabulate(6)(Fork)

@mpilquist
Copy link
Author

@oleg-py Thanks, I made that update along with a few other conveniences.

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