Last active
April 28, 2023 23:31
-
-
Save mpilquist/011a2ce4f55791f73b9566d6ea56830d to your computer and use it in GitHub Desktop.
Dining Philosophers with FS2
This file contains hidden or 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
/* | |
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) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I feel sudden desire to promote obscure functions from stdlib:
0.until(5).map(Fork(_)).toList
~List.tabulate(6)(Fork)