Run Futures in parallel in an actor - scala

I understand that using for comprehension on Futures we can make them run in parallel if the they are created outside the for block.
For example:
val f1 = Cloud.generateNumber(1)
val f2 = Cloud.generateNumber(2)
val f3 = Cloud.generateNumber(3)
val future = for {
a <- f1
b <- f2
c <- Future.successful(5 + 2)
d <- f3
} yield {
addInts(a,b,c,d)
}
The above code snippet would run f1, f2, and f3 in parallel.
Now consider the following sippets.
driver:
object DriverApp {
def main(args: Array[String]): Unit = {
val system = ActorSystem("MyActorSystem")
val parent = system.actorOf(Parent.props)
implicit val timeout: Timeout = 30.seconds
(parent ? GetMessageRequest()).mapTo[GetMessageResponse].map { resp =>
println("received response in driver")
println(resp.message)
}
Thread.sleep(5000)
system.shutdown()
}
}
parent:
class Parent extends Actor with ActorLogging {
val system = ActorSystem("MyActorSystem")
val child1Actor = system.actorOf(Child1.props, "child1-actor")
val child2Actor = system.actorOf(Child2.props, "child2-actor")
implicit val timeout: Timeout = 5.seconds
override def receive: Receive = {
case getFinalMessageRequest: GetMessageRequest => getFinalMessage(sender, getFinalMessageRequest)
}
private def getFinalMessage(sender: ActorRef, getFinalMessageRequest: GetMessageRequest): Unit = {
val r1 = getMessageFromC1()
val r2 = getMessageFromC2()
for {
i1 <- r1
i2 <- r2
} yield {
println("received response")
println(i1)
println(i2)
sender ! GetMessageResponse((i1 + i2).toString)
}
}
private def getMessageFromC1(): Future[Int] = {
import scala.concurrent.ExecutionContext.Implicits.global
Thread.sleep(Random.nextInt(500))
println("Executing getMessageFromC1")
(child1Actor ? GetMessageRequest()).mapTo[Int].map(v => v)
}
private def getMessageFromC2(): Future[Int] = {
import scala.concurrent.ExecutionContext.Implicits.global
Thread.sleep(Random.nextInt(500))
println("Executing getMessageFromC2")
(child2Actor ? GetMessageRequest()).mapTo[Int].map(v => v)
}
}
child1:
object Child1 {
final val props = Props[Child1]
case class GetMessageRequest2()
}
class Child1 extends Actor with ActorLogging{
override def receive: Receive = {
case getMessageRequest: GetMessageRequest => getFinalMessage(sender, getMessageRequest)
}
private def getFinalMessage(sender: ActorRef, getFinalMessageRequest: GetMessageRequest): Unit = {
println("in child actor 1")
sender ! 5
}
}
child2:
object Child2 {
final val props = Props[Child2]
}
class Child2 extends Actor with ActorLogging {
override def receive: Receive = {
case getFinalMessageRequest: GetMessageRequest => getFinalMessage(sender, getFinalMessageRequest)
}
private def getFinalMessage(sender: ActorRef, getFinalMessageRequest: GetMessageRequest): Unit = {
println("in child actor 2")
sender ! 10
}
}
For the second example the futures are executed sequentially. I've tested the the app quite a few times, on every occasion it results in the following output
Executing getMessageFromC1
in child actor 1
Executing getMessageFromC2
in child actor 2
received response
5
10
received response in driver
15
Is there some restriction that keeps us from running futures in parallel within actors?

Related

Why is my Actor created 2 times

I wrote this code
class TestActor extends Actor {
override def preStart(): Unit = {
println("going to start my test actor")
}
override def postStop(): Unit = {
println("came inside stop")
}
def receive = {
case msg: TestMessage => sender ! s"Hello ${msg.name}"
}
}
object TestActor {
val props = Props(new TestActor)
case class TestMessage(name: String)
}
I call it using this client code
object MyApp extends App {
val ac = ActorSystem("TestActorSystem")
val a = new ClassA(ac).sayHello()
val b = new ClassB(ac).sayHello()
for {
msg1 <- a
msg2 <- b
} {
println(msg1)
println(msg1)
}
Await.result(ac.terminate(), Duration.Inf)
}
class ClassA(ac: ActorSystem) {
def sayHello(): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorOf(TestActor.props)
val msg = actor ? TestActor.TestMessage("foo")
msg.map(_.asInstanceOf[String])
}
}
class ClassB(ac: ActorSystem) {
def sayHello() : Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorOf(TestActor.props)
val msg = actor ? TestActor.TestMessage("bar")
msg.map(_.asInstanceOf[String])
}
}
I see the output
going to start my test actor
going to start my test actor
Hello foo
Hello foo
came inside stop
came inside stop
My question is that in the companion object I had created the props object as a val and therefore there was only 1 val and that 1 val had 1 instance of new TestActor
In the client both classes used the same instance of actor system. Therefore there should have been only 1 actor and both messages from classA and ClassB should have gone to the same actor.
But it seems that both classes got their own Actor instances.
My question is that in the companion object I had created the props object as a val and therefore there was only 1 val and that 1 val had 1 instance of new TestActor
Not really. Yes, you define one val, but that val is a Props. The Props class is essentially a recipe for creating an actor. What you're defining is a single immutable recipe for creating a TestActor. This recipe can be used multiple times, which is what you're doing when you call ac.actorOf(TestActor.props) twice. Both of these calls use the same Props recipe to create a new TestActor; that is, you use the same recipe to create two TestActor instances.
To reuse a single TestActor, do what #mfirry suggested and create this actor outside of ClassA and ClassB. Here's one way to do that:
object MyApp extends App {
val ac = ActorSystem("TestActorSystem")
val testActor = ac.actorOf(TestActor.props)
val a = new ClassA(ac).sayHello(testActor)
val b = new ClassB(ac).sayHello(testActor)
for {
msg1 <- a
msg2 <- b
} {
println(msg1)
println(msg1)
}
Await.result(ac.terminate(), Duration.Inf)
}
class ClassA(ac: ActorSystem) {
def sayHello(actor: ActorRef): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val msg = actor ? TestActor.TestMessage("foo")
msg.map(_.asInstanceOf[String])
}
}
class ClassB(ac: ActorSystem) {
def sayHello(actor: ActorRef): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val msg = actor ? TestActor.TestMessage("bar")
msg.map(_.asInstanceOf[String])
}
}
This is caused by actorOf will create a new actor, so for twice actorOf it will create 2 TestActor. you can use actorSelection to avoid the second creation, like:
class ClassA(ac: ActorSystem) {
def sayHello(): Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorOf(Props[TestActor], "test")
println(actor.path)
val msg = actor ? TestMessage("foo")
msg.map(_.asInstanceOf[String])
}
}
class ClassB(ac: ActorSystem) {
def sayHello() : Future[String] = {
implicit val timeout = Timeout(5 seconds)
val actor = ac.actorSelection("akka://TestActorSystem/user/test")
val msg = actor ? TestMessage("bar")
msg.map(_.asInstanceOf[String])
}
}

Scala Akka actors, ask pattern, dead letters encountered while sending reply

I am trying to send a request to remote actor using ask pattern. The local actor recieves some value and it performs some task on it and updates it.
Then when local actor try to send back the updated value to remote actor , error occurs while sending. How should i handle this error?
Error:
[INFO] [03/31/2017 17:28:18.383] [ClientSystem-akka.actor.default-dispatcher-3] [akka://ClientSystem/deadLetters]
Message [check.package$Rcvdcxt] from Actor[akka://ClientSystem/user/localA1#1050660737] to Actor[akka://ClientSystem/deadLetters] was not delivered.
[1] dead letters encountered.
Remote Actor:
class RemoteActor() extends Actor {
def receive = {
case TaskFromLocal() =>{
implicit val timeout: Timeout = 15000
val currentSender = sender
val f1 = currentSender ? RemoteActor.rtree.cxtA
f1.onComplete{
case Success(Rcvdcxt(cxtA))=>
println("Success"+cxtA)
case Success(s) =>
println("Success :"+s)
case Failure(ex) =>
println("failure:"+ex)
}
}
case _ => println("unknown msg")
}
}
object RemoteActor{
def createRndCxtC(count: Int):List[CxtC] = (for (i <- 1 to count) yield CxtC(Random.nextString(5), Random.nextInt())).toList
def createRndCxtB(count: Int): List[CxtB] = (for (i <- 1 to count) yield CxtB(createRndCxtC(count), Random.nextInt())).toList
def createRndCxtA(count: Int): List[CxtA] = (for (i <- 1 to count) yield CxtA(createRndCxtC(count), 5)).toList
var rtree = RCxt(createRndCxtA(1),createRndCxtB(2),1,"")
def main(args: Array[String]) {
val configFile = getClass.getClassLoader.getResource("remote_application.conf").getFile
val config = ConfigFactory.parseFile(new File(configFile))
val system = ActorSystem("RemoteSystem" , config)
val remoteActor = system.actorOf(Props[RemoteActor], name="remote")
println("remote is ready")
}
}
Local Actor :
class LocalActorA extends Actor{
#throws[Exception](classOf[Exception])
val remoteActor = context.actorSelection("akka.tcp://RemoteSystem#127.0.0.1:5150/user/remote")
def receive = {
case TaskLA1(taskA) => {
implicit val timeout: Timeout = 15000
val rCxt = remoteActor ? TaskFromLocal()
val currentSender = sender
rCxt.onComplete{
case Success(Rcvdcxt(cxtA))=>
println("Success"+cxtA)
println("Sender: "+ sender)
currentSender ! Rcvdcxt(cxtA)
case Success(s)=>
println("Got nothing from Remote"+s)
currentSender ! "Failuree"
case Failure(ex) =>
println("Failure in getting remote")
currentSender ! "Failure"
}
}
}
}
object LocalActorA {
def createRndCxtC(count: Int):List[CxtC] = (for (i <- 1 to count) yield CxtC(Random.nextString(5), Random.nextInt())).toList
def createRndCxtB(count: Int): List[CxtB] = (for (i <- 1 to count) yield CxtB(createRndCxtC(count), Random.nextInt())).toList
def createRndCxtA(count: Int): List[CxtA] = (for (i <- 1 to count) yield CxtA(createRndCxtC(count), 3)).toList
var tree = RCxt(createRndCxtA(2),createRndCxtB(2),1,"")
def main(args: Array[String]) {
val configFile = getClass.getClassLoader.getResource("local_application.conf").getFile
val config = ConfigFactory.parseFile(new File(configFile))
val system = ActorSystem("ClientSystem",config)
val localActorA1 = system.actorOf(Props[LocalActorA], name="localA1")
println("LocalActor A tree : "+tree)
localActorA1 ! TaskLA1(new DummySum())
}
}
Since you didn't post all the code I can't really tell exactly the error, but my best guess is related to the fact that you are calling sender in the onComplete in the LocalActor. This is unsafe and should be avoided at all costs. Instead, do something similar with the remote actor:
class LocalActor {
def receive = {
case TaskLA1(taskA) =>
val currentSender = sender
rCxt.onComplete {
case Success(Rcvdcxt(cxtA))=>
currentSender ! Rcvdcxt(cxtA)
...
}
}
}

PersistentActor cannot invoke persist handler in Future onComplete

I am pretty new using PersistentActor ,
when I try to call updateState from a future onComplete, fails , nothing happanes , tried to debug it and I do get to the persist call but not into the updateState
trait Event
case class Cmd(data: String)
case class Evt(data: String) extends Event
class BarActor extends PersistentActor{
implicit val system = context.system
implicit val executionContext = system.dispatcher
def updateState(event: Evt): Unit ={
println("Updating state")
state = state.updated(event)
sender() ! state
}
def timeout(implicit ec: ExecutionContext) =
akka.pattern.after(duration = 2 seconds, using = system.scheduler)(Future.failed(new TimeoutException("Got timed out!")))
val receiveCommand: Receive = {
case Cmd(data) =>
def anotherFuture(i: Int)(implicit system: ActorSystem) = {
val realF = Future {
i % 2 match {
case 0 =>
Thread.sleep(100)
case _ =>
Thread.sleep(500)
}
i
}
Future.firstCompletedOf(Seq(realF, timeout))
.recover {
case _ => -1
}
}
val res = (1 to 10).map(anotherFuture(_))
val list = Future.sequence(res)
list.onComplete{
case _ =>
persist(Evt("testing"))(updateState)
}
}
}
You could try this:
list.onComplete {
case _ => self ! Evt("testing")
}
And add this to receiveCommand
case evt: Evt =>
persist(Evt("testing"))(updateStates)

Passing implicit ExecutionContext to contained objects/called methods

I'm creating an async library using Scala 2.10 futures. The constructor for the library takes a sequence of user-defined objects that implement a certain trait, and then a method on the library class sends some data one-by-one into the user-defined objects. I want the user to provide the ExecutionContext for the async operations when setting up the main instance, and then for that context to get passed into the user-defined objects as necessary. Simplified (pseudo?)code:
case class Response(thing: String)
class LibraryObject(stack: Seq[Processor])(implicit context: ExecutionContext) {
def entryPoint(data: String): Future[Response] = {
val response = Future(Response(""))
stack.foldLeft(response) { (resp, proc) => proc.process(data, resp) }
}
}
trait Processor {
def process(data: String, resp: Future[Response]): Future[Response]
}
It might be used something like this:
class ThingProcessor extends Processor {
override def process(data: String, response: Future[Response]) = {
response map { _.copy(thing = "THE THING") }
}
}
class PassThroughProcessor extends Processor {
override def process(request: Request, response: Future[Response]) = {
response
}
}
object TheApp extends App {
import ExecutionContext.Implicits.global
val stack = List(
new ThingProcessor,
new PassThroughProcessor
)
val libObj = new LibraryObject(stack)
val futureResponse = libObj.entryPoint("http://some/url")
// ...
}
I get a compile error for ThingProcessor:
Cannot find an implicit ExecutionContext, either require one yourself or import ExecutionContext.Implicits.global
My question is, how do I implicitly supply the ExecutionContext that LibraryObject has to the user-defined objects (ThingProcessor and PassThroughProcessor) or their methods without making the user (who will be writing the classes) worry about it--that is to say, I would prefer that the user did not have to type:
class MyFirstProcessor(implicit context: ExecutionContext)
or
override def process(...)(implicit context: ExecutionContext) = { ... }
The implicit scope includes companion objects and type parameters of base classes.
Or, library.submit(new library.Processor { def process() ... }).
This works, but wasn't my first thought, which was to be more clever:
import concurrent._
import concurrent.duration._
class Library(implicit xc: ExecutionContext = ExecutionContext.global) {
trait Processor {
implicit val myxc: ExecutionContext = xc
def process(i: Future[Int]): Future[Int]
}
def submit(p: Processor) = p process future(7)
}
object Test extends App {
val library = new Library
val p = new library.Processor {
def process(i: Future[Int]) = for (x <- i) yield 2 * x
}
val res = library submit p
val z = Await result (res, 10.seconds)
Console println z
}
Update:
import concurrent._
import concurrent.duration._
import java.util.concurrent.Executors
class Library()(implicit xc: ExecutionContext = ExecutionContext.global) {
trait Processor {
implicit val myxc: ExecutionContext = xc
def process(i: Future[Int]): Future[Int]
}
def submit(p: Processor) = p process future(7)
}
object ctx {
val xc = ExecutionContext fromExecutorService Executors.newSingleThreadExecutor
}
object library1 extends Library
object library2 extends Library()(ctx.xc)
object p extends library1.Processor {
def process(i: Future[Int]) = for (x <- i) yield 2 * x
}
object q extends library2.Processor {
def process(i: Future[Int]) = for (x <- i) yield 3 * x
}
object Test extends App {
val res = library1 submit p
//val oops = library2 submit p
//val oops = library1 submit q
val z = Await result (res, 10.seconds)
Console println z
Console println (Await result (library2 submit q, 10.seconds))
ctx.xc.shutdownNow()
}
It isn't much of a stretch to:
class Library(implicit xc: ExecutionContext = ExecutionContext.global) {
def submit(p: Processor): Future[Int] = p dueProcess future(7)
}
trait Processor {
implicit var myxc: ExecutionContext = _
def dueProcess(i: Future[Int])(implicit xc: ExecutionContext) = {
myxc = xc
process(i)
}
protected def process(i: Future[Int]): Future[Int]
}
object ctx {
val xc = ExecutionContext fromExecutorService Executors.newSingleThreadExecutor
}
object Test extends App {
def db() = Console println (new Throwable().getStackTrace mkString ("TRACE [\n ", "\n ", "\n]"))
val library = new Library()(ctx.xc)
val p = new Processor {
protected def process(i: Future[Int]) = for (x <- i) yield { db(); 2 * x }
}
val res = library submit p
val z = Await result (res, 10.seconds)
Console println z
ctx.xc.shutdownNow()
}

Implement weak-referenced Eventbus actors?

Is it correct to say that unreferenced actors remain subscribed to the event stream ? At least, that's what I get from experimenting with Akka...
I'm trying to implement weak-referencing for actors in an EventBus scenario. In those cases the event listeners/actors typically come and go. Unlike independent actors which are supposed to be present all of the time. Explicit unregistering of course does work. But I'm not always able to perceive the right moment to do this.
Does Akka provide in such a use case ?
val as = ActorSystem.create("weak")
var actor = as.actorOf(Props[ExceptionHandler])
as.eventStream.subscribe(actor,classOf[Exception])
// an event is published & received
as.eventStream.publish(new KnownProblem)
//session expires or whatever that makes the actor redundant
actor = null
(1 to 30).foreach(_ => System.gc)
// an event is published & STILL received
as.eventStream.publish(new KnownProblem)
Okay, I could not actually implement it, but actor is stopping on GC. Using Scala 2.9.2 (REPL) + Akka 2.0.3.
The EventBus with WeakReference[ActorRef] did not help - because in Akka you also have a dungeon with ChildrenContainer (self.children), also there could be Monitor subscriptions to lifecycle events. The thing I did not try - creating actors with dispatcher that only knows about our new shiny WeakEventBus - so maybe I missed the point?
Here goes the code for REPL (start it with appropriate imports, and :paste it in 2 steps):
// Start REPL with something like:
// scala -Yrepl-sync -classpath "/opt/akka-2.0.3/lib/akka/akka-actor-2.0.3.jar:
// /opt/akka-2.0.3/lib/akka/akka-remote-2.0.3.jar:
// /opt/akka-2.0.3/lib/akka/config-0.3.1.jar:
// /opt/akka-2.0.3/lib/akka/protobuf-java-2.4.1.jar:
// /opt/akka-2.0.3/lib/akka/netty-3.5.3.Final.jar"
// :paste 1/2
import akka.actor._
import akka.pattern._
import akka.event._
import akka.util._
import com.typesafe.config.ConfigFactory
import akka.util.Timeout
import akka.dispatch.Await
import scala.ref.WeakReference
import java.util.Comparator
import java.util.concurrent.atomic._
import java.util.UUID
case class Message(val id:String,val timestamp: Long)
case class PostMessage(
override val id:String=UUID.randomUUID().toString(),
override val timestamp: Long=new java.util.Date().getTime(),
text:String) extends Message(id, timestamp)
case class MessageEvent(val channel:String, val message:Message)
case class StartServer(nodeName: String)
case class ServerStarted(nodeName: String, actor: ActorRef)
case class IsAlive(nodeName: String)
case class IsAliveWeak(nodeName: String)
case class AmAlive(nodeName: String, actor: ActorRef)
case class GcCheck()
case class GcCheckScheduled(isScheduled: Boolean,
gcFlag: WeakReference[AnyRef])
trait WeakLookupClassification { this: WeakEventBus ⇒
protected final val subscribers = new Index[Classifier,
WeakReference[Subscriber]](mapSize(),
new Comparator[WeakReference[Subscriber]] {
def compare(a: WeakReference[Subscriber],
b: WeakReference[Subscriber]): Int = {
if (a.get == None || b.get == None) -1
else compareSubscribers(a.get.get, b.get.get)
}
})
protected def mapSize(): Int
protected def compareSubscribers(a: Subscriber, b: Subscriber): Int
protected def classify(event: Event): Classifier
protected def publish(event: Event, subscriber: Subscriber): Unit
def subscribe(subscriber: Subscriber, to: Classifier): Boolean =
subscribers.put(to, new WeakReference(subscriber))
def unsubscribe(subscriber: Subscriber, from: Classifier): Boolean =
subscribers.remove(from, new WeakReference(subscriber))
def unsubscribe(subscriber: Subscriber): Unit =
subscribers.removeValue(new WeakReference(subscriber))
def publish(event: Event): Unit = {
val i = subscribers.valueIterator(classify(event))
while (i.hasNext) publish(event, i.next().get.get)
}
}
class WeakEventBus extends EventBus with WeakLookupClassification {
type Event = MessageEvent
type Classifier=String
type Subscriber = ActorRef
protected def compareSubscribers(a: ActorRef, b: ActorRef) = a compareTo b
protected def mapSize(): Int = 10
protected def classify(event: Event): Classifier = event.channel
protected def publish(event: Event, subscriber: Subscriber): Unit =
subscriber ! event
}
lazy val weakEventBus = new WeakEventBus
implicit val timeout = akka.util.Timeout(1000)
lazy val actorSystem = ActorSystem("serversys", ConfigFactory.parseString("""
akka {
loglevel = "DEBUG"
actor {
provider = "akka.remote.RemoteActorRefProvider"
debug {
receive = on
autoreceive = on
lifecycle = on
event-stream = on
}
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
log-sent-messages = on
log-received-messages = on
}
}
serverconf {
include "common"
akka {
actor {
deployment {
/root {
remote = "akka://serversys#127.0.0.1:2552"
}
}
}
remote {
netty {
hostname = "127.0.0.1"
port = 2552
}
}
}
}
""").getConfig("serverconf"))
class Server extends Actor {
private[this] val scheduled = new AtomicBoolean(false)
private[this] val gcFlagRef = new AtomicReference[WeakReference[AnyRef]]()
val gcCheckPeriod = Duration(5000, "millis")
override def preRestart(reason: Throwable, message: Option[Any]) {
self ! GcCheckScheduled(scheduled.get, gcFlagRef.get)
super.preRestart(reason, message)
}
def schedule(period: Duration, who: ActorRef) =
actorSystem.scheduler.scheduleOnce(period)(who ! GcCheck)
def receive = {
case StartServer(nodeName) =>
sender ! ServerStarted(nodeName, self)
if (scheduled.compareAndSet(false, true))
schedule(gcCheckPeriod, self)
val gcFlagObj = new AnyRef()
gcFlagRef.set(new WeakReference(gcFlagObj))
weakEventBus.subscribe(self, nodeName)
actorSystem.eventStream.unsubscribe(self)
case GcCheck =>
val gcFlag = gcFlagRef.get
if (gcFlag == null) {
sys.error("gcFlag")
}
gcFlag.get match {
case Some(gcFlagObj) =>
scheduled.set(true)
schedule(gcCheckPeriod, self)
case None =>
println("Actor stopped because of GC: " + self)
context.stop(self)
}
case GcCheckScheduled(isScheduled, gcFlag) =>
if (isScheduled && scheduled.compareAndSet(false, isScheduled)) {
gcFlagRef.compareAndSet(null, gcFlag)
schedule(gcCheckPeriod, self)
}
case IsAlive(nodeName) =>
println("Im alive (default EventBus): " + nodeName)
sender ! AmAlive(nodeName, self)
case e: MessageEvent =>
println("Im alive (weak EventBus): " + e)
}
}
// :paste 2/2
class Root extends Actor {
def receive = {
case start # StartServer(nodeName) =>
val server = context.actorOf(Props[Server], nodeName)
context.watch(server)
Await.result(server ? start, timeout.duration)
.asInstanceOf[ServerStarted] match {
case started # ServerStarted(nodeName, _) =>
sender ! started
case _ =>
throw new RuntimeException(
"[S][FAIL] Could not start server: " + start)
}
case isAlive # IsAlive(nodeName) =>
Await.result(context.actorFor(nodeName) ? isAlive,
timeout.duration).asInstanceOf[AmAlive] match {
case AmAlive(nodeName, _) =>
println("[S][SUCC] Server is alive : " + nodeName)
case _ =>
throw new RuntimeException("[S][FAIL] Wrong answer: " + nodeName)
}
case isAliveWeak # IsAliveWeak(nodeName) =>
actorSystem.eventStream.publish(MessageEvent(nodeName,
PostMessage(text="isAlive-default")))
weakEventBus.publish(MessageEvent(nodeName,
PostMessage(text="isAlive-weak")))
}
}
lazy val rootActor = actorSystem.actorOf(Props[Root], "root")
object Root {
def start(nodeName: String) = {
val msg = StartServer(nodeName)
var startedActor: Option[ActorRef] = None
Await.result(rootActor ? msg, timeout.duration)
.asInstanceOf[ServerStarted] match {
case succ # ServerStarted(nodeName, actor) =>
println("[S][SUCC] Server started: " + succ)
startedActor = Some(actor)
case _ =>
throw new RuntimeException("[S][FAIL] Could not start server: " + msg)
}
startedActor
}
def isAlive(nodeName: String) = rootActor ! IsAlive(nodeName)
def isAliveWeak(nodeName: String) = rootActor ! IsAliveWeak(nodeName)
}
////////////////
// actual test
Root.start("weak")
Thread.sleep(7000L)
System.gc()
Root.isAlive("weak")