Skip to content
Snippets Groups Projects
Commit 1b1b466e authored by Matt Bovel's avatar Matt Bovel
Browse files

Add solutions 5

parent 6bd59da1
No related branches found
No related tags found
No related merge requests found
.vscode
.metals
.bloop
.bsp
target
metals.sbt
build.properties
project/project
version = "3.4.0"
runner.dialect = scala3
rewrite.scala3.convertToNewSyntax = true
rewrite.scala3.removeOptionalBraces = true
# Exercise Session 5, Solutions
## Problem 1: Message Processing Semantics
### Problem 1.1
For the `Client1` actor, the only possible output is `0`. The reason is that messages between two actors are guaranteed to be received in the order they were sent.
You can try the code yourself by running:
```
sbt "runMain problem1_1"
```
### Problem 1.2
1. For the `Client2` actor, either `0` or `1` can be printed. There are no restrictions on the order in which messages are processed in this case. It might be the case that the `Memory` actor receives the `Write` message from the `Client2` first, or the `Read` message from the `Proxy` first.
2. The order in which the messages are sent by the `Client2` doesn't change the possible behaviours of the system.
3. In the case both messages are sent through the `Proxy`, then the only possible output is `0`, since in this case the messages between the `Client2` and `Proxy`, as well as between `Proxy` and `Memory`, are guaranteed to be handled in the same order they were sent.
You can try the code yourself by running:
```
sbt "runMain problem1_2"
```
## Problem 2
The solution is in `Problem2.scala`.
You can run it using:
```
sbt "runMain problem2 10"
```
val scala3Version = "3.1.2"
val akkaVersion = "2.6.19"
lazy val root = project
.in(file("."))
.settings(
name := "code",
version := "0.1.0-SNAPSHOT",
scalaVersion := scala3Version,
fork := true,
javaOptions ++= Seq(
"-Dakka.loglevel=Info",
"-Dakka.actor.allow-java-serialization=on"
),
libraryDependencies ++= Seq(
"org.scalameta" %% "munit" % "1.0.0-M3" % Test,
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-testkit" % akkaVersion,
"com.typesafe.akka" %% "akka-persistence" % akkaVersion,
// SLF4J backend
// See https://doc.akka.io/docs/akka/current/typed/logging.html#slf4j-backend
"ch.qos.logback" % "logback-classic" % "1.2.11"
),
Test / testOptions += Tests.Argument(TestFrameworks.JUnit)
)
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
import scala.util.{Try, Success, Failure}
import scala.concurrent.ExecutionContext
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicReference
import akka.actor.*
import akka.testkit.*
import akka.event.LoggingReceive
enum Protocol:
case Write(value: Int)
case Read(requester: ActorRef)
import Protocol.*
enum Responses:
case Answer(value: Int)
import Responses.*
class Memory extends Actor:
var value = 0
override def receive: Receive = {
case Write(newValue) => value = newValue
case Read(requester) => requester ! Answer(value)
}
class Client(memory: ActorRef) extends Actor:
override def receive: Receive = { case Answer(value) =>
println(value)
}
class MyProxy(memory: ActorRef) extends Actor:
override def receive: Receive = { case message =>
memory ! message
}
@main def problem1_1 =
for _ <- 1 to 1000 do
val system = ActorSystem("example")
try
val memory = system.actorOf(Props(Memory()))
val client = system.actorOf(Props(Client(memory)))
memory ! Read(client)
memory ! Write(1)
finally system.terminate()
@main def problem1_2 =
for _ <- 1 to 1000 do
val system = ActorSystem("example")
try
val memory = system.actorOf(Props(Memory()))
val proxy = system.actorOf(Props(MyProxy(memory)))
val client = system.actorOf(Props(Client(memory)))
proxy ! Read(client)
memory ! Write(1)
finally system.terminate()
import akka.actor.*
import akka.testkit.TestKit
object Soldier:
// The different messages that can be sent between the actors:
enum Protocol:
// The recipient should die.
case Death
// The recipient should update its next reference.
case Next(next: ActorRef)
// The recipient should act.
case Act
class Soldier(number: Int) extends Actor:
import Soldier.*
import Protocol.*
def receive: Receive = behavior(None, None, false)
def behavior(
next: Option[ActorRef],
killer: Option[ActorRef],
mustAct: Boolean
): Receive = {
case Death =>
next match
case Some(myNext) =>
sender() ! Next(myNext)
myNext ! Act
println("Soldier " + number + " dies.")
self ! PoisonPill
case None =>
context.become(
behavior(next = None, killer = Some(sender()), mustAct = mustAct)
)
case Next(newNext) =>
if newNext == self then println("Soldier " + number + " is last !")
else if !killer.isEmpty then
killer.get ! Next(newNext)
newNext ! Act
println("Soldier " + number + " dies.")
self ! PoisonPill
else if mustAct then
newNext ! Death
context.become(behavior(next = None, killer = None, mustAct = false))
else
context.become(
behavior(next = Some(newNext), killer = None, mustAct = false)
)
case Act =>
next match
case Some(myNext) =>
myNext ! Death
context.become(
behavior(next = None, killer = killer, mustAct = false)
)
case None =>
context.become(behavior(next = None, killer = killer, mustAct = true))
}
@main def problem2(n: Int) = new TestKit(ActorSystem()):
import Soldier.*
import Soldier.Protocol.*
// Initialization
require(n >= 1)
// Creation of the actors.
val actors = Seq.tabulate(n)(
(i: Int) => system.actorOf(Props(classOf[Soldier], i), "Soldier" + i)
)
// Inform all actors of the next actor in the circle.
for i <- 0 to (n - 2) do actors(i) ! Next(actors(i + 1))
actors(n - 1) ! Next(actors(0))
// Inform the first actor to start acting.
actors(0) ! Act
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment