Issue: #22453 Mode inlined example for Shared Mutable State to its own class Add additional wrong case when the message is mutable Includes auto reformated code
80 lines
2.2 KiB
Scala
80 lines
2.2 KiB
Scala
/**
|
|
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
|
|
*/
|
|
package docs.actor
|
|
|
|
class SharedMutableStateDocSpec {
|
|
|
|
//#mutable-state
|
|
import akka.actor.{ Actor, ActorRef }
|
|
import akka.pattern.ask
|
|
import akka.util.Timeout
|
|
import scala.concurrent.Future
|
|
import scala.concurrent.duration._
|
|
import scala.language.postfixOps
|
|
import scala.collection.mutable
|
|
|
|
case class Message(msg: String)
|
|
|
|
class EchoActor extends Actor {
|
|
def receive = {
|
|
case msg => sender() ! msg
|
|
}
|
|
}
|
|
|
|
class CleanUpActor extends Actor {
|
|
def receive = {
|
|
case set: mutable.Set[_] => set.clear()
|
|
}
|
|
}
|
|
|
|
class MyActor(echoActor: ActorRef, cleanUpActor: ActorRef) extends Actor {
|
|
var state = ""
|
|
val mySet = mutable.Set[String]()
|
|
|
|
def expensiveCalculation(actorRef: ActorRef): String = {
|
|
// this is a very costly operation
|
|
"Meaning of live is 42"
|
|
}
|
|
|
|
def expensiveCalculation(): String = {
|
|
// this is a very costly operation
|
|
"Meaning of live is 42"
|
|
}
|
|
|
|
def receive = {
|
|
case _ =>
|
|
|
|
//Wrong ways
|
|
implicit val ec = context.dispatcher
|
|
implicit val timeout = Timeout(5 seconds) // needed for `?` below
|
|
|
|
// Very bad, shared mutable state,
|
|
// will break your application in weird ways
|
|
Future { state = "This will race" }
|
|
((echoActor ? Message("With this other one")).mapTo[Message])
|
|
.foreach { received => state = received.msg }
|
|
|
|
// Very bad, shared mutable object,
|
|
// the other actor cand mutate your own state,
|
|
// or worse, you might get weird race conditions
|
|
cleanUpActor ! mySet
|
|
|
|
// Very bad, "sender" changes for every message,
|
|
// shared mutable state bug
|
|
Future { expensiveCalculation(sender()) }
|
|
|
|
//Right ways
|
|
|
|
// Completely safe, "self" is OK to close over
|
|
// and it's an ActorRef, which is thread-safe
|
|
Future { expensiveCalculation() } foreach { self ! _ }
|
|
|
|
// Completely safe, we close over a fixed value
|
|
// and it's an ActorRef, which is thread-safe
|
|
val currentSender = sender()
|
|
Future { expensiveCalculation(currentSender) }
|
|
}
|
|
}
|
|
//#mutable-state
|
|
}
|