/* % scalac DiningHakkersOnFsm.scala; % scala DiningHakkersOnFsm * minor mods made of activator template DiningHakkersOnFsm.scala * read http://192.168.17.223/pmateti/Courses/7370/ * Lectures/Actors+Akka+Scala/dining-philosophers-akka-fsm.html */ import akka.actor._, akka.actor.FSM._ , scala.concurrent.duration._ sealed trait ChopstickMessage object CmsgGrab extends ChopstickMessage object CmsgRetn extends ChopstickMessage case class CmsgTaken(chopstick: ActorRef) extends ChopstickMessage case class CmsgBusy(chopstick: ActorRef) extends ChopstickMessage sealed trait ChopstickState case object CSAvailabe extends ChopstickState case object CSTaken extends ChopstickState case class IsWith(hakker: ActorRef) class Chopstick extends Actor with FSM[ChopstickState, IsWith] { import context._ startWith(CSAvailabe, IsWith(system.deadLetters)) when(CSAvailabe) { case Event(CmsgGrab, _) => goto(CSTaken) using IsWith(sender) replying CmsgTaken(self) } when(CSTaken) { case Event(CmsgGrab, currentState) => stay replying CmsgBusy(self) case Event(CmsgRetn, IsWith(hakker)) if sender == hakker => goto(CSAvailabe) using IsWith(system.deadLetters) } initialize } sealed trait HakkerMessage object Think extends HakkerMessage sealed trait HakkerState case object Waiting extends HakkerState case object Thinking extends HakkerState case object Hungry extends HakkerState case object WaitForOtherChopstick extends HakkerState case object FirstChopstickDenied extends HakkerState case object Eating extends HakkerState case class Possess(left: Option[ActorRef], right: Option[ActorRef]) class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor with FSM[HakkerState, Possess] { startWith(Waiting, Possess(None, None)) when(Waiting) { case Event(Think, _) => println( s"$name starts to think" ) startThinking(5.seconds) } when(Thinking) { case Event(StateTimeout, _) => left ! CmsgGrab right ! CmsgGrab goto(Hungry) } when(Hungry) { case Event(CmsgTaken(`left`), _) => goto(WaitForOtherChopstick) using Possess(Some(left), None) case Event(CmsgTaken(`right`), _) => goto(WaitForOtherChopstick) using Possess(None, Some(right)) case Event(CmsgBusy(_), _) => goto(FirstChopstickDenied) } when(WaitForOtherChopstick) { case Event(CmsgTaken(`left`), Possess(None, Some(right))) => startEating(left, right) case Event(CmsgTaken(`right`), Possess(Some(left), None)) => startEating(left, right) case Event(CmsgBusy(chopstick), Possess(leftOption, rightOption)) => leftOption.foreach(_ ! CmsgRetn) rightOption.foreach(_ ! CmsgRetn) startThinking(10.milliseconds) } private def startEating(left: ActorRef, right: ActorRef) = { println(s"$name has picked up ${left.path.name} and ${right.path.name} and starts to eat") goto(Eating) using Possess(Some(left), Some(right)) forMax (5.seconds) } when(FirstChopstickDenied) { case Event(CmsgTaken(secondChopstick), _) => secondChopstick ! CmsgRetn startThinking(10.milliseconds) case Event(CmsgBusy(chopstick), _) => startThinking(10.milliseconds) } when(Eating) { case Event(StateTimeout, _) => println(s"$name puts chopsticks down and starts to think") left ! CmsgRetn right ! CmsgRetn startThinking(5.seconds) } initialize private def startThinking(duration: FiniteDuration): State = { goto(Thinking) using Possess(None, None) forMax duration } } object DiningHakkersOnFsm { val acsy = ActorSystem() def main(args: Array[String]) { val chopsticks = for (i <- 0 to 4) yield acsy.actorOf(Props[Chopstick], "C" + i) val hakkers = for { (name, i) <- List("P0", "P1", "P2", "P3", "P4").zipWithIndex } yield acsy.actorOf( Props(classOf[FSMHakker], name, chopsticks(i), chopsticks((i + 1) % 5))) hakkers.foreach(_ ! Think) } }