/* % 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)
}
}