diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50ad4a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.ammonite +.metals \ No newline at end of file diff --git a/ActorDemo.sc b/ActorDemo.sc index dd489ba..c82a23e 100755 --- a/ActorDemo.sc +++ b/ActorDemo.sc @@ -13,35 +13,47 @@ import akka.util.Timeout import scala.concurrent.duration._ import akka.actor.typed.scaladsl.AskPattern._ import scala.concurrent.ExecutionContext.Implicits.global +import akka.actor.typed.scaladsl.AbstractBehavior +import akka.actor.typed.scaladsl.ActorContext +import scala.util.Success +import scala.util.Failure +import scala.concurrent.Await +import scala.concurrent.Future object CounterActor { - sealed trait Command - final case object IncCount extends Command final case class CounterResult(count: Int) - final case class GetResult(sender: ActorRef[RootActor.Command]) - extends Command - var counter = 0 + sealed trait Command + final case object IncCount extends Command + final case class GetResult(sender: ActorRef[CounterResult]) extends Command def apply(): Behavior[Command] = - Behaviors.receive { (context, command) => - command match { - case IncCount => counter += 1 + Behaviors.setup(ctx => new CounterActor(ctx)) + + private class CounterActor(context: ActorContext[Command]) + extends AbstractBehavior[Command](context) { + private var counter = 0 // encapsulated mutable state + override def onMessage(msg: Command): Behavior[Command] = { + msg match { + case IncCount => + counter += 1 + Behaviors.same case GetResult(sender) => context.log.info(s"Value of counter is $counter"); - sender ! RootActor.Reply(counter) + sender ! CounterResult(counter) + Behaviors.same } - - Behaviors.same } + } } object RootActor { sealed trait Command - case class Reply[T](reply: T) extends Command - case object Begin extends Command - case object GetResult extends Command + final case class Reply[T](reply: T) extends Command + final case object Begin extends Command + final case class GetResult(repyTo: ActorRef[CounterActor.CounterResult]) + extends Command implicit val timeout: Timeout = 3.seconds @@ -63,10 +75,11 @@ object RootActor { counterActor ! CounterActor.IncCount counterActor ! CounterActor.IncCount - case GetResult => + case GetResult(replyTo) => counterActor ! CounterActor.GetResult( - context.self + replyTo ) + } Behaviors.same @@ -77,13 +90,15 @@ object RootActor { @main def main() = { - implicit val rootActor = + implicit val system = ActorSystem(RootActor(), "TestActors") implicit val timeout: Timeout = 3.seconds - rootActor ! RootActor.Begin - rootActor ! RootActor.GetResult + system ! RootActor.Begin + // system ! RootActor.GetResult - Thread.sleep(1000) + val x: Future[CounterActor.CounterResult] = + system ? (ref => RootActor.GetResult(ref)) + Await.result(x, 1.second) } diff --git a/README.md b/README.md index 5cb088b..30db07a 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,15 @@ An experiment to see how the actor model can be used to synchronize / encapsulate mutable state without the use of locks. ## Usage -``` bash -./ActorsDemo.sc -``` -Sample output: +```bash +./ActorsDemo.sc ``` -22:11:47.473 [TestActors-akka.actor.default-dispatcher-3] INFO ammonite.$file.ActorDemo$RootActor$ - --- Beginning --- -22:11:47.475 [TestActors-akka.actor.default-dispatcher-6] INFO ammonite.$file.ActorDemo$CounterActor$ - Value of counter is 5 +Sample output: -22:11:47.475 [TestActors-akka.actor.default-dispatcher-3] INFO ammonite.$file.ActorDemo$RootActor$ - Received message Reply(5) -``` \ No newline at end of file +``` +13:02:58.966 [TestActors-akka.actor.default-dispatcher-3] INFO ammonite.$file.AkkaActorsDemo.ActorDemo$RootActor$ - --- Beginning --- +13:02:58.967 [TestActors-akka.actor.default-dispatcher-5] INFO ammonite.$file.AkkaActorsDemo.ActorDemo$CounterActor$CounterActor - Value of counter is 5 +CounterResult(5) +```