+sam #3689 Make activator template of the fsm sample
This commit is contained in:
parent
23b9fad153
commit
e6f679fe8b
12 changed files with 138 additions and 157 deletions
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>.
|
||||
*/
|
||||
package sample.become
|
||||
|
||||
import akka.actor._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
// Akka adaptation of
|
||||
// http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
||||
|
||||
/*
|
||||
* First we define our messages, they basically speak for themselves
|
||||
*/
|
||||
sealed trait DiningHakkerMessage
|
||||
case class Busy(chopstick: ActorRef) extends DiningHakkerMessage
|
||||
case class Put(hakker: ActorRef) extends DiningHakkerMessage
|
||||
case class Take(hakker: ActorRef) extends DiningHakkerMessage
|
||||
case class Taken(chopstick: ActorRef) extends DiningHakkerMessage
|
||||
object Eat extends DiningHakkerMessage
|
||||
object Think extends DiningHakkerMessage
|
||||
|
||||
/*
|
||||
* A Chopstick is an actor, it can be taken, and put back
|
||||
*/
|
||||
class Chopstick extends Actor {
|
||||
|
||||
import context._
|
||||
|
||||
//When a Chopstick is taken by a hakker
|
||||
//It will refuse to be taken by other hakkers
|
||||
//But the owning hakker can put it back
|
||||
def takenBy(hakker: ActorRef): Receive = {
|
||||
case Take(otherHakker) =>
|
||||
otherHakker ! Busy(self)
|
||||
case Put(`hakker`) =>
|
||||
become(available)
|
||||
}
|
||||
|
||||
//When a Chopstick is available, it can be taken by a hakker
|
||||
def available: Receive = {
|
||||
case Take(hakker) =>
|
||||
become(takenBy(hakker))
|
||||
hakker ! Taken(self)
|
||||
}
|
||||
|
||||
//A Chopstick begins its existence as available
|
||||
def receive = available
|
||||
}
|
||||
|
||||
/*
|
||||
* A hakker is an awesome dude or dudett who either thinks about hacking or has to eat ;-)
|
||||
*/
|
||||
class Hakker(name: String, left: ActorRef, right: ActorRef) extends Actor {
|
||||
|
||||
import context._
|
||||
|
||||
//When a hakker is thinking it can become hungry
|
||||
//and try to pick up its chopsticks and eat
|
||||
def thinking: Receive = {
|
||||
case Eat =>
|
||||
become(hungry)
|
||||
left ! Take(self)
|
||||
right ! Take(self)
|
||||
}
|
||||
|
||||
//When a hakker is hungry it tries to pick up its chopsticks and eat
|
||||
//When it picks one up, it goes into wait for the other
|
||||
//If the hakkers first attempt at grabbing a chopstick fails,
|
||||
//it starts to wait for the response of the other grab
|
||||
def hungry: Receive = {
|
||||
case Taken(`left`) =>
|
||||
become(waiting_for(right, left))
|
||||
case Taken(`right`) =>
|
||||
become(waiting_for(left, right))
|
||||
case Busy(chopstick) =>
|
||||
become(denied_a_chopstick)
|
||||
}
|
||||
|
||||
//When a hakker is waiting for the last chopstick it can either obtain it
|
||||
//and start eating, or the other chopstick was busy, and the hakker goes
|
||||
//back to think about how he should obtain his chopsticks :-)
|
||||
def waiting_for(chopstickToWaitFor: ActorRef, otherChopstick: ActorRef): Receive = {
|
||||
case Taken(`chopstickToWaitFor`) =>
|
||||
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
|
||||
become(eating)
|
||||
system.scheduler.scheduleOnce(5.seconds, self, Think)
|
||||
|
||||
case Busy(chopstick) =>
|
||||
otherChopstick ! Put(self)
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
//When the results of the other grab comes back,
|
||||
//he needs to put it back if he got the other one.
|
||||
//Then go back and think and try to grab the chopsticks again
|
||||
def denied_a_chopstick: Receive = {
|
||||
case Taken(chopstick) =>
|
||||
chopstick ! Put(self)
|
||||
startThinking(10.milliseconds)
|
||||
case Busy(chopstick) =>
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
//When a hakker is eating, he can decide to start to think,
|
||||
//then he puts down his chopsticks and starts to think
|
||||
def eating: Receive = {
|
||||
case Think =>
|
||||
left ! Put(self)
|
||||
right ! Put(self)
|
||||
println("%s puts down his chopsticks and starts to think".format(name))
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
//All hakkers start in a non-eating state
|
||||
def receive = {
|
||||
case Think =>
|
||||
println("%s starts to think".format(name))
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
private def startThinking(duration: FiniteDuration): Unit = {
|
||||
become(thinking)
|
||||
system.scheduler.scheduleOnce(duration, self, Eat)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Alright, here's our test-harness
|
||||
*/
|
||||
object DiningHakkersOnBecome {
|
||||
val system = ActorSystem()
|
||||
|
||||
def main(args: Array[String]): Unit = run()
|
||||
|
||||
def run(): Unit = {
|
||||
//Create 5 chopsticks
|
||||
val chopsticks = for (i <- 1 to 5) yield system.actorOf(Props[Chopstick], "Chopstick" + i)
|
||||
|
||||
//Create 5 awesome hakkers and assign them their left and right chopstick
|
||||
val hakkers = for {
|
||||
(name, i) <- List("Ghosh", "Boner", "Klang", "Krasser", "Manie").zipWithIndex
|
||||
} yield system.actorOf(Props(classOf[Hakker], name, chopsticks(i), chopsticks((i + 1) % 5)))
|
||||
|
||||
//Signal all hakkers that they should start thinking, and watch the show
|
||||
hakkers.foreach(_ ! Think)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>.
|
||||
*/
|
||||
package sample.fsm
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.FSM._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
// Akka adaptation of
|
||||
// http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
|
||||
|
||||
/*
|
||||
* Some messages for the chopstick
|
||||
*/
|
||||
sealed trait ChopstickMessage
|
||||
object Take extends ChopstickMessage
|
||||
object Put extends ChopstickMessage
|
||||
case class Taken(chopstick: ActorRef) extends ChopstickMessage
|
||||
case class Busy(chopstick: ActorRef) extends ChopstickMessage
|
||||
|
||||
/**
|
||||
* Some states the chopstick can be in
|
||||
*/
|
||||
sealed trait ChopstickState
|
||||
case object Available extends ChopstickState
|
||||
case object Taken extends ChopstickState
|
||||
|
||||
/**
|
||||
* Some state container for the chopstick
|
||||
*/
|
||||
case class TakenBy(hakker: ActorRef)
|
||||
|
||||
/*
|
||||
* A chopstick is an actor, it can be taken, and put back
|
||||
*/
|
||||
class Chopstick extends Actor with FSM[ChopstickState, TakenBy] {
|
||||
import context._
|
||||
|
||||
// A chopstick begins its existence as available and taken by no one
|
||||
startWith(Available, TakenBy(system.deadLetters))
|
||||
|
||||
// When a chopstick is available, it can be taken by a some hakker
|
||||
when(Available) {
|
||||
case Event(Take, _) =>
|
||||
goto(Taken) using TakenBy(sender) replying Taken(self)
|
||||
}
|
||||
|
||||
// When a chopstick is taken by a hakker
|
||||
// It will refuse to be taken by other hakkers
|
||||
// But the owning hakker can put it back
|
||||
when(Taken) {
|
||||
case Event(Take, currentState) =>
|
||||
stay replying Busy(self)
|
||||
case Event(Put, TakenBy(hakker)) if sender == hakker =>
|
||||
goto(Available) using TakenBy(system.deadLetters)
|
||||
}
|
||||
|
||||
// Initialze the chopstick
|
||||
initialize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Some fsm hakker messages
|
||||
*/
|
||||
sealed trait FSMHakkerMessage
|
||||
object Think extends FSMHakkerMessage
|
||||
|
||||
/**
|
||||
* Some fsm hakker states
|
||||
*/
|
||||
sealed trait FSMHakkerState
|
||||
case object Waiting extends FSMHakkerState
|
||||
case object Thinking extends FSMHakkerState
|
||||
case object Hungry extends FSMHakkerState
|
||||
case object WaitForOtherChopstick extends FSMHakkerState
|
||||
case object FirstChopstickDenied extends FSMHakkerState
|
||||
case object Eating extends FSMHakkerState
|
||||
|
||||
/**
|
||||
* Some state container to keep track of which chopsticks we have
|
||||
*/
|
||||
case class TakenChopsticks(left: Option[ActorRef], right: Option[ActorRef])
|
||||
|
||||
/*
|
||||
* A fsm hakker is an awesome dude or dudette who either thinks about hacking or has to eat ;-)
|
||||
*/
|
||||
class FSMHakker(name: String, left: ActorRef, right: ActorRef) extends Actor with FSM[FSMHakkerState, TakenChopsticks] {
|
||||
|
||||
//All hakkers start waiting
|
||||
startWith(Waiting, TakenChopsticks(None, None))
|
||||
|
||||
when(Waiting) {
|
||||
case Event(Think, _) =>
|
||||
println("%s starts to think".format(name))
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
//When a hakker is thinking it can become hungry
|
||||
//and try to pick up its chopsticks and eat
|
||||
when(Thinking) {
|
||||
case Event(StateTimeout, _) =>
|
||||
left ! Take
|
||||
right ! Take
|
||||
goto(Hungry)
|
||||
}
|
||||
|
||||
// When a hakker is hungry it tries to pick up its chopsticks and eat
|
||||
// When it picks one up, it goes into wait for the other
|
||||
// If the hakkers first attempt at grabbing a chopstick fails,
|
||||
// it starts to wait for the response of the other grab
|
||||
when(Hungry) {
|
||||
case Event(Taken(`left`), _) =>
|
||||
goto(WaitForOtherChopstick) using TakenChopsticks(Some(left), None)
|
||||
case Event(Taken(`right`), _) =>
|
||||
goto(WaitForOtherChopstick) using TakenChopsticks(None, Some(right))
|
||||
case Event(Busy(_), _) =>
|
||||
goto(FirstChopstickDenied)
|
||||
}
|
||||
|
||||
// When a hakker is waiting for the last chopstick it can either obtain it
|
||||
// and start eating, or the other chopstick was busy, and the hakker goes
|
||||
// back to think about how he should obtain his chopsticks :-)
|
||||
when(WaitForOtherChopstick) {
|
||||
case Event(Taken(`left`), TakenChopsticks(None, Some(right))) => startEating(left, right)
|
||||
case Event(Taken(`right`), TakenChopsticks(Some(left), None)) => startEating(left, right)
|
||||
case Event(Busy(chopstick), TakenChopsticks(leftOption, rightOption)) =>
|
||||
leftOption.foreach(_ ! Put)
|
||||
rightOption.foreach(_ ! Put)
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
private def startEating(left: ActorRef, right: ActorRef): State = {
|
||||
println("%s has picked up %s and %s and starts to eat".format(name, left.path.name, right.path.name))
|
||||
goto(Eating) using TakenChopsticks(Some(left), Some(right)) forMax (5.seconds)
|
||||
}
|
||||
|
||||
// When the results of the other grab comes back,
|
||||
// he needs to put it back if he got the other one.
|
||||
// Then go back and think and try to grab the chopsticks again
|
||||
when(FirstChopstickDenied) {
|
||||
case Event(Taken(secondChopstick), _) =>
|
||||
secondChopstick ! Put
|
||||
startThinking(10.milliseconds)
|
||||
case Event(Busy(chopstick), _) =>
|
||||
startThinking(10.milliseconds)
|
||||
}
|
||||
|
||||
// When a hakker is eating, he can decide to start to think,
|
||||
// then he puts down his chopsticks and starts to think
|
||||
when(Eating) {
|
||||
case Event(StateTimeout, _) =>
|
||||
println("%s puts down his chopsticks and starts to think".format(name))
|
||||
left ! Put
|
||||
right ! Put
|
||||
startThinking(5.seconds)
|
||||
}
|
||||
|
||||
// Initialize the hakker
|
||||
initialize()
|
||||
|
||||
private def startThinking(duration: FiniteDuration): State = {
|
||||
goto(Thinking) using TakenChopsticks(None, None) forMax duration
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Alright, here's our test-harness
|
||||
*/
|
||||
object DiningHakkersOnFsm {
|
||||
|
||||
val system = ActorSystem()
|
||||
|
||||
def main(args: Array[String]): Unit = run()
|
||||
|
||||
def run(): Unit = {
|
||||
// Create 5 chopsticks
|
||||
val chopsticks = for (i <- 1 to 5) yield system.actorOf(Props[Chopstick], "Chopstick" + i)
|
||||
// Create 5 awesome fsm hakkers and assign them their left and right chopstick
|
||||
val hakkers = for {
|
||||
(name, i) <- List("Ghosh", "Boner", "Klang", "Krasser", "Manie").zipWithIndex
|
||||
} yield system.actorOf(Props(classOf[FSMHakker], name, chopsticks(i), chopsticks((i + 1) % 5)))
|
||||
|
||||
hakkers.foreach(_ ! Think)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue