From bcc74df2ac8994eca361be5f3c1bb0155755df8e Mon Sep 17 00:00:00 2001 From: momania Date: Wed, 24 Nov 2010 15:44:42 +0100 Subject: [PATCH] - re-add fsm samples - removed ton of whitespaces from the project definition --- .../main/scala/DiningHakkersOnBecome.scala | 139 +++ .../src/main/scala/DiningHakkersOnFsm.scala | 172 +++ project/build/AkkaProject.scala | 978 +----------------- 3 files changed, 314 insertions(+), 975 deletions(-) create mode 100644 akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala create mode 100644 akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala diff --git a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala new file mode 100644 index 0000000000..c762c2da7d --- /dev/null +++ b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnBecome.scala @@ -0,0 +1,139 @@ +package sample.fsm.dining.become + +//Akka adaptation of +//http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/ + +import akka.actor.{Scheduler, ActorRef, Actor} +import akka.actor.Actor._ +import java.util.concurrent.TimeUnit + +/* + * 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(name: String) extends Actor { + self.id = name + + //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 { + self.id = name + + //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`) => + log.info("%s has picked up %s and %s, and starts to eat",name,left.id,right.id) + become(eating) + Scheduler.scheduleOnce(self,Think,5,TimeUnit.SECONDS) + + case Busy(chopstick) => + become(thinking) + otherChopstick ! Put(self) + self ! Eat + } + + //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) => + become(thinking) + chopstick ! Put(self) + self ! Eat + case Busy(chopstick) => + become(thinking) + self ! Eat + } + + //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 => + become(thinking) + left ! Put(self) + right ! Put(self) + log.info("%s puts down his chopsticks and starts to think",name) + Scheduler.scheduleOnce(self,Eat,5,TimeUnit.SECONDS) + } + + //All hakkers start in a non-eating state + def receive = { + case Think => + log.info("%s starts to think",name) + become(thinking) + Scheduler.scheduleOnce(self,Eat,5,TimeUnit.SECONDS) + } +} + +/* + * Alright, here's our test-harness + */ +object DiningHakkers { + def run { + //Create 5 chopsticks + val chopsticks = for(i <- 1 to 5) yield actorOf(new Chopstick("Chopstick "+i)).start + //Create 5 awesome hakkers and assign them their left and right chopstick + val hakkers = for { + (name,i) <- List("Ghosh","Bonér","Klang","Krasser","Manie").zipWithIndex + } yield actorOf(new Hakker(name,chopsticks(i),chopsticks((i+1) % 5))).start + + //Signal all hakkers that they should start thinking, and watch the show + hakkers.foreach(_ ! Think) + } +} diff --git a/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala new file mode 100644 index 0000000000..34bc3ba29e --- /dev/null +++ b/akka-samples/akka-sample-fsm/src/main/scala/DiningHakkersOnFsm.scala @@ -0,0 +1,172 @@ +package sample.fsm.dining.fsm + +import akka.actor.{ActorRef, Actor, FSM} +import Actor._ +import java.util.concurrent.TimeUnit +import TimeUnit._ + +/* + * 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: Option[ActorRef]) + +/* + * A chopstick is an actor, it can be taken, and put back + */ +class Chopstick(name: String) extends Actor with FSM[ChopstickState, TakenBy] { + self.id = name + + // When a chopstick is available, it can be taken by a some hakker + when(Available) { + case Event(Take, _) => + goto(Taken) using TakenBy(self.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 self.sender == hakker => + goto(Available) using TakenBy(None) + } + + // A chopstick begins its existence as available and taken by no one + startWith(Available, TakenBy(None)) +} + +/** + * 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] { + self.id = name + + when(Waiting) { + case Event(Think, _) => + log.info("%s starts to think", 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 = { + log.info("%s has picked up %s and %s, and starts to eat", name, left.id, right.id) + 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, _) => + log.info("%s puts down his chopsticks and starts to think", name) + left ! Put + right ! Put + startThinking(5, SECONDS) + } + + private def startThinking(period: Int, timeUnit: TimeUnit): State = { + goto(Thinking) using TakenChopsticks(None, None) forMax (period, timeUnit) + } + + //All hakkers start waiting + startWith(Waiting, TakenChopsticks(None, None)) +} + +/* + * Alright, here's our test-harness + */ +object DiningHakkersOnFsm { + + def run = { + // Create 5 chopsticks + val chopsticks = for (i <- 1 to 5) yield actorOf(new Chopstick("Chopstick " + i)).start + // Create 5 awesome fsm hakkers and assign them their left and right chopstick + val hakkers = for{ + (name, i) <- List("Ghosh", "Bonér", "Klang", "Krasser", "Manie").zipWithIndex + } yield actorOf(new FSMHakker(name, chopsticks(i), chopsticks((i + 1) % 5))).start + + hakkers.foreach(_ ! Think) + } +} diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index fbbf09627e..58cfaf185a 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -406,12 +406,15 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { } class AkkaSampleRemoteProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) + class AkkaSampleFSMProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) { override def disableCrossPaths = true lazy val akka_sample_ants = project("akka-sample-ants", "akka-sample-ants", new AkkaSampleAntsProject(_), akka_stm) + lazy val akka_sample_fsm = project("akka-sample-fsm", "akka-sample-fsm", + new AkkaSampleFSMProject(_), akka_actor) lazy val akka_sample_remote = project("akka-sample-remote", "akka-sample-remote", new AkkaSampleRemoteProject(_), akka_remote) } @@ -499,978 +502,3 @@ trait DeployProject { self: BasicScalaProject => trait OSGiProject extends BNDPlugin { self: DefaultProject => override def bndExportPackage = Seq("akka.*;version=%s".format(projectVersion.value)) } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -