The cases in the act method of the main actor are never matched in this code, so my wrapUp method is never called.
import java.net.Socket
import scala.actors._
import Actor._
import scala.collection.mutable.ArrayBuffer
object LowPortScanner {
var lastPort = 0
var openPorts = ArrayBuffer[Int]()
var longestRunTime = 00.00
var results = List[Tuple3[Int, Range, Long]]()
val host = "localhost"
val numProcs = 1 to Runtime.getRuntime().availableProcessors()
val portsPerProc = 1024 / numProcs.size
val caller = self
val procPortRanges = numProcs.foldLeft(List[Tuple2[Int, Range]]()) { (portRanges, proc) =>
val tuple2 = (proc.toInt, (lastPort + 1) to (lastPort + portsPerProc))
lastPort += portsPerProc
tuple2 :: portRanges
}
def main(args: Array[String]): Unit = {
//spawn an actor for each processor that scans a given port range
procPortRanges.foreach { proc =>
actor {
caller ! scan(proc._1, proc._2)
} //end inner actors
} //end numProcs.foreach
//catch results from the processor actors above
def act {
loop {
reactWithin(100) {
//update the list of results returned from scan
case scanResult: Tuple3[Int, Range, Long] =>
println("Processor " + scanResult._1 + " completed scan of ports " + scanResult._2.first + " through " + scanResult._2.last)
results = results ::: List(scanResult)
//check if results have been returned for each actor
case TIMEOUT =>
println("Main actor timed out")
if (results.size == numProcs.size) wrapUp
case _ =>
println("got back something weird from one of the port scan actors!")
wrapUp
}
}
}
//Attempt to open a socket on each port in the given range
//returns a Tuple3[procID: Int, ports: Range, time: Long
def scan(proc: Int, ports: Range) = {
val startTime = System.nanoTime()
ports.foreach { n =>
try {
//println("Processor " + proc + " is checking port " + n)
print(".")
val socket = new Socket(host, n)
//println("Found open port: " + n)
openPorts += n
socket.close
} catch {
case e: Exception =>
//println("While scanning port " + n + " caught Exception: " + e)
}
}
(proc, ports, startTime - System.nanoTime())
}
//output results and kill the main actor
def wrapUp {
println("These are the open ports in the range 1-1024:")
openPorts.foreach { port => println(port) }
results.foreach { result => if (result._3 > longestRunTime) { longestRunTime = result._3 } }
println("Time to scan ports 1 through 1024 is: %3.3f".format(longestRunTime / 1000))
caller ! exit
}
}
}
The code that spawns off the actors for each processor works fine, the scan method is invoked correctly for each range of ports, and I have confirmed that exactly 1024 ports are scanned.
My expectation is that the line :
caller ! scan(proc._1, proc._2)
should send a message containing (Int, Range, Long) back to the main actor. The main actor's act method should then catch that message and execute:
case scanResult: Tuple3[Int, Range, Long] =>
println("Processor " + scanResult._1 + " completed scan of ports " + scanResult._2.first + " through " + scanResult._2.last)
results = results ::: List(scanResult)
This is not happening, however. In fact, as far as I can tell, none of my messages are coming back to the main actor. I'm not clear on what I'm missing or doing wrong.
The problem as you note is that the message being sent are not being received. This is because the actor created with actor {caller ! scan(proc._1, proc._2)} does not have an act definition associated with it. The def act{...} method has nothing to do with the actor created since it is a method on LowPortScanner and not an actor.
Maintaining the spirit of your code you can define the body to be executed by the actor within the actor{...} and assign it to a value and send it messages.
//spawn an actor for each processor that scans a given port range
procPortRanges.foreach { proc =>
val myactor = actor {
//catch results from the processor actors above
loop {
reactWithin(100) {
//update the list of results returned from scan
case scanResult: Tuple3[Int, Range, Long] =>
println("Processor " + scanResult._1 + " completed scan of ports " + scanResult._2.first + " through " + scanResult._2.last)
results = results ::: List(scanResult)
//check if results have been returned for each actor
case TIMEOUT =>
println("Main actor timed out")
if (results.size == numProcs.size) wrapUp
case _ =>
println("got back something weird from one of the port scan actors!")
wrapUp
}
}
//catch results from the processor actors above
} //end inner actors
myactor ! scan(proc._1, proc._2)
} //end numProcs.foreach
Another way to do it is to extend the Actor trait. This way the def act{...} will handle messages received by the LowPortScanner actor. I also did some minor refactoring including using this instead of self. The Actor also had to be started with this.start().
import java.net.Socket
import scala.actors._
import Actor._
import scala.collection.mutable.ArrayBuffer
object LowPortScanner extends Actor {
var lastPort = 0
var openPorts = ArrayBuffer[Int]()
var longestRunTime = 00.00
var results = List[Tuple3[Int, Range, Long]]()
val host = "localhost"
val numProcs = 1 to Runtime.getRuntime().availableProcessors()
val portsPerProc = 1024 / numProcs.size
val procPortRanges = numProcs.foldLeft(List[Tuple2[Int, Range]]()) { (portRanges, proc) =>
val tuple2 = (proc.toInt, (lastPort + 1) to (lastPort + portsPerProc))
lastPort += portsPerProc
tuple2 :: portRanges
}
//catch results from the processor actors above
def act {
loop {
reactWithin(100) {
//update the list of results returned from scan
case scanResult: Tuple3[Int, Range, Long] =>
println("Processor " + scanResult._1 + " completed scan of ports " + scanResult._2.first + " through " + scanResult._2.last)
results = results ::: List(scanResult)
//check if results have been returned for each actor
case TIMEOUT =>
println("Main actor timed out")
if (results.size == numProcs.size) wrapUp
case _ =>
println("got back something weird from one of the port scan actors!")
wrapUp
}
}
}
//Attempt to open a socket on each port in the given range
//returns a Tuple3[procID: Int, ports: Range, time: Long
def scan(proc: Int, ports: Range) = {
val startTime = System.nanoTime()
ports.foreach { n =>
try {
//println("Processor " + proc + " is checking port " + n)
print(".")
val socket = new Socket(host, n)
//println("Found open port: " + n)
openPorts += n
socket.close
} catch {
case e: Exception =>
//println("While scanning port " + n + " caught Exception: " + e)
}
}
(proc, ports, startTime - System.nanoTime())
}
//output results and kill the main actor
def wrapUp {
println("These are the open ports in the range 1-1024:")
openPorts.foreach { port => println(port) }
results.foreach { result => if (result._3 > longestRunTime) { longestRunTime = result._3 } }
println("Time to scan ports 1 through 1024 is: %3.3f".format(longestRunTime / 1000))
this ! exit
}
def main(args: Array[String]): Unit = {
//spawn an actor for each processor that scans a given port range
this.start()
procPortRanges.foreach { proc =>
actor {
this ! scan(proc._1, proc._2)
} //end inner actors
} //end numProcs.foreach
}
}
Here are the results from a run:
scala> LowPortScanner.main(Array[String]())
scala> ...Processor 6 completed scan of ports 641 through 768
...Processor 5 completed scan of ports 513 through 640
...Processor 4 completed scan of ports 385 through 512
...Processor 3 completed scan of ports 257 through 384
...Processor 1 completed scan of ports 1 through 128
...Processor 7 completed scan of ports 769 through 896
...Processor 2 completed scan of ports 129 through 256
...Processor 8 completed scan of ports 897 through 1024
Main actor timed out
These are the open ports in the range 1-1024:
139
22
445
591
631
111
Time to scan ports 1 through 1024 is: 0.000
scala> LowPortScanner.results
res2: List[(Int, Range, Long)] = List((6,Range(641, 642, 643,...
Related
I am looking to expand the following code to work for an unknown amount of actor ask requests.
implicit val timeout = Timeout(100 millis)
val sendRequestActor = context.actorOf(Props(new SendRequest(request)), "Send_Request_".concat(getActorNumber))
val sendRequestActor2 = context.actorOf(Props(new SendRequest(request)), "Send_Request_".concat(getActorNumber))
val a1 = ask(sendRequestActor, Request).fallbackTo(Future.successful(RequestTimeout))
val a2 = ask(sendRequestActor2, Request).fallbackTo(Future.successful(RequestTimeout))
val result = for {
r1 <- a1
r2 <- a2
} yield(r1, r2)
val r = Await.result(result, 100 millis)
r match {
case (b: SuccessResponse, b2: SuccessResponse) => {
//Process Results
}
case (b: SuccessResponse, b2: RequestTimeout) => {
//Process Results
}
case (b: RequestTimeout, b2: SuccessResponse) => {
//Process Results
}
case (b: RequestTimeout, b2: RequestTimeout) => {
//Process Results
}
case _ => {}
}
I am trying to send out requests to a List of recipients(gotten from a previous database call). The number of recipients will vary each time this function is called. Recipients have a maximum of 100 milliseconds to respond before I want to time out their requests and record a RequestTimeout. The SendRequest actor will reply with SuccessResponse if the recipients respond. I am assuming I will have to change the val result for-loop to process a list, but I am unsure of how to structure everything so that I will wait the minimum amount of time(either when all actors return or when the timeout hits, whichever is lower). I do not need everything in a single return value like the example, I am fine with a list of results and matching type on each iteration.
Any help would be appreciated, please let me know if I can provide any other information.
Thank you
Edit:
Calling Class:
case object GetResponses
def main(args: Array[String]) {
val route = {
get {
complete {
//stuff
val req_list = List(req1,req2,req3)
val createRequestActor = system.actorOf(Props(new SendAll(req_list)), "Get_Response_Actor_" + getActorNumber)
val request_future = ask(createRequestActor, GetResponses).mapTo[List[Any]]
Thread.sleep(1000)
println(request_future)
//more stuff
}
}
}
Http().bindAndHandle(route, "localhost", 8080)
}
Updated Sending Class:
class SendAll(requests: List[Request]) extends Actor {
import context.{become,dispatcher}
var numProcessed = 0
var results: List[Any] = List()
requests.foreach(self ! _)
implicit val timeout = Timeout(100 millis)
def receive = {
case r: RequestMsg =>
val sendRequestActor = context.actorOf(Props(new SendRequest(r)), "Send_Request_".concat(getActorNumber))
(sendRequestActor ? Request).pipeTo(self)
case s: SuccessResponse =>
println("Got Success")
results = results :+ s
println(results.size + " == " + requests.size)
if(results.size == requests.size) {
println("Before done")
become(done)
}
case akka.actor.Status.Failure(f) =>
println("Got Failed")
results = results :+ RequestTimeout
if(results.size == requests.size) {
become(done)
}
case m =>
println("Got Other")
}
def done: Receive = {
case GetResponses =>
println("Done")
sender ! results
case _ => {
println("Done as well")
}
}
}
Output
Got Success
1 == 3
Got Success
2 == 3
Got Success
3 == 3
Before done
Future(<not completed>)
I would pass the list of requests to the actor, then pipe the responses from the child actors to self instead of using Await.result. For example:
class Handler(requests: List[RequestMsg]) extends Actor {
import context.{become, dispatcher}
var numProcessed = 0
var results: List[Any] = List()
requests.foreach(self ! _)
implicit val timeout = Timeout(100.millis)
def receive = {
case r: RequestMsg =>
val sendRequestActor = context.actorOf(Props(new SendRequest(r)), "Send_Request".concat(getActorNumber))
(sendRequestActor ? Request).pipeTo(self)
case s: SuccessResponse =>
println(s"response: $s")
results = results :+ s
if (results.size == requests.size)
become(done)
case akka.actor.Status.Failure(f) =>
println("a request failed or timed out")
results = results :+ RequestTimeout
if (results.size == requests.size)
become(done)
case m =>
println(s"Unhandled message received while processing requests: $m")
sender ! NotDone
}
def done: Receive = {
case GetResponses =>
println("sending responses")
sender ! results
}
}
You would instantiate an actor for every list of requests:
val requests1 = List(RequestMsg("one"), RequestMsg("two"), RequestMsg("three"))
val handler1 = system.actorOf(Props(new Handler(requests1)))
In this example--following the principle that an actor should have a distinct, limited sphere of responsibility--the actor simply coordinates requests and responses; it doesn't perform any processing on the collected responses. The idea is that another actor would send this actor a GetResponses messages in order to get the responses and process them (or this actor would proactively send the results to a processing actor).
The simplest solution is put all your actor refs into the List map it to List[Future] and use Future.sequence to obtain Future[List].
val route = {
get {
val listActorRefs = List(actorRef1, actorRef2, ...)
val futureListResponses = Future.sequence(listActorRefs.map(_ ? Request))
onComplete(futureListResponses) {
case Success(listResponse) => ...
complete(...)
case Failure(exception) => ...
}
}
}
A better solution is avoid a lot of actor' asks, prepare some ResponseCollector actor which will send all your message (I suggest to look at BroadcastPool) and schedule one message for itself to stop waiting and return result.
I want to do something like this:
For every item in the collection, ask an actor and block while waiting for the response for some timeout interval, if the time out exception is thrown, I want to move on to the next item.
Here's the code pattern:
implicit val timeout: akka.util.Timeout = 3 seconds
collection.foreach { item =>
val future = (actor ? Request(msg = item)).mapTo[Response]
future.onComplete {
case Failure(ex) => // log ex
case Success(resp) => // use resp
}
Await.result(future, 3 seconds)
}
The actor is itself calling other actors which might take longer than my 3 second time out.
This doesn't work as expected: after the first item times out, the whole thing crashes and stops. There are some dead letter notifications, I suppose that's because when the actors that my actor is calling finish, the original sender is invalid (it took more than 3 seconds). So my question is how do I tell it to just forget the time out item and continue with the rest as if nothing happened?
#stefanobaghino is right. See here, as written in documentation if a future contains an exception then Await.result throws is so that it can be handled properly.
Here you are matching Failure case of future but you are not recovering from it. A better approach would be like following -
collection.foreach { item =>
val future = (actor ? Request(msg = item)).mapTo[Response]
future.recover {
case ex: Exception =>
// log ex
Response(ex.message) // some other object of type Response
}
val response = Await.result(future, 3 seconds)
// use response here
}
After reading answer by #Dimitri I tried logging timestamps in milliseconds to see where it was causing lag in whole process and i found rather strange behavior. I observed that whenever there were dead-letters there was huge lag in even starting processing of next message to actor. Not sure why this is happening. Below is the code i tried to check it -
package com.lightbend.akka.sample
import akka.actor.{ Actor, ActorLogging, ActorRef, ActorSystem, Props }
import akka.pattern.{ ask, pipe, AskTimeoutException }
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.io.StdIn
import scala.util.{ Try, Success, Failure }
import scala.concurrent.ExecutionContext.Implicits.global
import java.util.concurrent.TimeoutException
object AkkaQuickStart {
class NumberActor extends Actor {
override def receive: Receive = {
case (num: Int, startAt: Long) =>
println("B " + num.toString + " : " + System.currentTimeMillis().toString + " : " + (System.currentTimeMillis() - startAt).toString)
Thread.sleep(500 * num)
sender() ! "OK"
}
}
def main(args: Array[String]): Unit = {
implicit val timeout: akka.util.Timeout = 1 seconds
val numActor = ActorSystem("system").actorOf(Props(new NumberActor()))
val range = (1 to 5) ++ (4 to 1 by -1)
println(range)
def lag(implicit startAt: Long): String = (System.currentTimeMillis() - startAt).toString
range.map { r =>
implicit val startAt = System.currentTimeMillis()
println("A " + r.toString + " : " + System.currentTimeMillis().toString + " : " + lag)
val future = (numActor ? (r, startAt))
.recover {
case ex: AskTimeoutException =>
println("E " + r.toString + " : " + System.currentTimeMillis().toString + " : " + lag)
"Ask timeout"
}
.mapTo[String]
future.onComplete{
case Success(reply) =>
println("C " + r.toString + " : " + System.currentTimeMillis().toString + " : " + lag + " : success " + reply)
case Failure(reply) =>
println("C " + r.toString + " : " + System.currentTimeMillis().toString + " : " + lag + " : failure")
}
Try(Await.result(future, 1 seconds)) match {
case Success(reply) =>
println("D " + r.toString + " : " + System.currentTimeMillis().toString + " : " + lag + " : " + reply)
case Failure(ex) =>
println("D " + r.toString + " : " + System.currentTimeMillis().toString + " : " + lag + " : Await timeout ")
}
}
}
}
I tried different combinations of Ask timeout and Await timeout and found following lags in starting processing of actor message sent at the end of iteration -
Ask timeout = 1 Await Timeout = 1 => 3000 - 4500 ms causes dead-letters
Ask timeout = 1 Await Timeout = 3 => 3000 - 4500 ms causes dead-letters
Ask timeout = 3 Await Timeout = 1 => 3000 - 4500 ms causes dead-letters
Ask timeout = 3 Await timeout = 3 => 0 - 500 ms does not cause dead-letters
I am not sure but a guess is that dispatcher takes time in handling dead-letters and thus can not start processing messages of our Actor. May be some more experienced can explain it.
#stefanobaghino #Tarun Thanks for your help, I think I got it now.
So the thing is there are 2 timeouts that can cause an exception:
The Ask (?) timeout throws akka.pattern.AskTimeoutException if we have to wait longer than the actor takes to respond.
The Await.result throws java.util.concurrent.TimeoutException if we don't wait long enough for the future to finish.
Both of these can cause the whole thing to crash. For the first one, as you mentioned, we can add recover to return some default value. For the second one, we also should catch and handle the exception.
You can see different behaviours when changing the two timeouts and removing the recover/Try in the following example:
object Example {
class NumberActor extends Actor {
override def receive: Receive = {
case num: Int =>
Thread.sleep(250 * num)
sender() ! "OK"
}
}
def main(): Unit = {
implicit val timeout: akka.util.Timeout = 1 seconds
val numActor = ActorSystem("system").actorOf(Props(new NumberActor()))
val range = (1 to 5) ++ (4 to 1 by -1)
println(range)
range.map { r =>
val future = (numActor ? r)
.recover { case ex: TimeoutException => "FAIL" }
.mapTo[String]
Try(Await.result(future, 1 seconds)) match {
case Success(reply) => println(reply)
case Failure(ex) => println(ex)
}
}
}
}
Say I have a worker actor that receives a message, does a bit of processing and returns a result. And I have a sequence of messages that need to be converted into a sequence of results:
object Test {
case class Message(str: String)
case class Result(str: String)
class Worker extends Actor {
def receive = {
case Message(data) =>
println("Sleeping: " + data)
Thread.sleep(10000)
val result = Result(data + " - result")
println("Sending result: " + result)
sender ! result
}
}
def test(messages: Seq[Message]): Future[Seq[Result]] = {
val worker = ActorSystem().actorOf(Props(new Worker))
val results = messages.map { m =>
implicit val timeout = Timeout(20 seconds)
println("Sending: " + m)
val result = worker ? m
result.asInstanceOf[Future[Result]]
}
Future.sequence(results)
}
def main(args: Array[String]): Unit = {
val messages: Seq[Message] = args.map(Message(_))
test(messages).foreach { r =>
println("Result: " + r)
}
}
}
If I run the above with just "message-1" as an argument it runs fine giving the the output is below:
Sending: Message(message-1)
Sleeping: message-1
Sending result: Result(message-1 - result)
Result: ArraySeq(Result(message-1 - result))
However say I do it with: "message-1" "message-2" "message-3" then the last message ends up being sent to deadLetters:
Sending: Message(message-1) Sending: Message(message-2) Sleeping:
message-1 Sending: Message(message-3)
Sending result: Result(message-1 - result)
Sleeping: message-2
Sending result: Result(message-2 - result)
Sleeping: message-3
Sending result: Result(message-3 - result)
[INFO] [07/15/2016 09:07:49.832]
[default-akka.actor.default-dispatcher-2] [akka://default/deadLetters]
Message [util.Tester$Result] from
Actor[akka://default/user/$a#1776546850] to
Actor[akka://default/deadLetters] was not delivered. [1] dead letters
encountered. This logging can be turned off or adjusted with
configuration settings 'akka.log-dead-letters' and
'akka.log-dead-letters-during-shutdown'.
I am guessing this is because my calling thread has gone out of scope by the time the last message is sent. How can correctly collect all results into a sequence?
Note that changing my test method to below gives the same results:
def test(messages: Seq[Message]): Future[Seq[Result]] = {
val worker = ActorSystem().actorOf(Props(new Worker))
Future.traverse(messages) { m =>
implicit val timeout = Timeout(20 seconds)
println("Sending: " + m)
val result = worker ? m
result.asInstanceOf[Future[Result]]
}
}
The dumb answer is:
Future.traverse(messages)(m => actor ? m).map(_.asInstanceOf[Result])
But it might be better to send data all at once:
class Worker extends Actor {
def receive = {
case Message(data) =>
// Convert data into result
...
sender ! result
case seq: Seq[Message] =>
...
sender ! results
}
}
Seems to be because my timeout was set too low. Should have been large enough to cover all the work - for example 40 seconds.
I am working on a load generator app using akka actors. The app worked fine for few million requests but when increasing the load to more than say 10 million requests or run the load for a duration ( instead of number of requests) using a infinite loop the application hangs. Below is a simplified implementation and it just prints the command being tested. I also notice that the stats are not logged or the app does'nt shutdown when the time is over. I use the schedulers to dump stats every 30 secs and shutdown the app after 2hrs. Tested with small interval and do not see the processing of the "stats" and "Shutdown" messages.
Any idea what may be causing the application to hang ?
import akka.actor._
import akka.util.duration._
import akka.routing.RoundRobinRouter
import com.test.redload.util.CommandGenerator
import org.apache.log4j.Logger
import akka.util.Duration
class LoadWorker extends Actor {
val log = Logger.getLogger(this.getClass().getName())
def receive = {
case "PUT" => sender ! PUT
case "GET" => sender ! GET
case "DELETE" => sender ! DELETE
case "POST" => sender ! POST
case "HEAD" => sender ! HEAD
}
def PUT():Boolean = {println("PUT");return true}
def GET():Boolean = {println("GET");return true}
def DELETE():Boolean = {println("DELETE");return true}
def POST():Boolean = {println("POST");return true}
def HEAD():Boolean = {println("HEAD");return true}
}
class LoadGenerator(nrOfWorkers:Int, noOfMessages:Int) extends Actor {
val log = Logger.getLogger(this.getClass().getName())
val start:Long = System.currentTimeMillis
var noOfMessageRcvd:Int = 0
val r = new CommandGenerator// <- is basically are list implementation that iterates and returns the next command
r.addCommand("PUT",5) r.addCommand("GET",2) r.addCommand("DELETE",2)
r.addCommand("POST",2) r.addCommand("HEAD",1) r.addCommand("LBRPOP",1)
val loadRouter = context.actorOf(Props[LoadWorker].withRouter(RoundRobinRouter(nrOfWorkers)),name ="loadRouter")
def receive = {
case "start" => {
if(noOfMessages > 1) {
for( i <- 0 until noOfMessages) loadRouter ! r.getRandomCommand()
} else {
log.info("Time bound Load run..")
//for( i <- 0 until 10000000) { //<- For any number greater than few millions that app hangs after few messages
while(true){loadRouter ! r.getRandomCommand() //<- with while loop the app hangs as soon as it begins
}
}
}
case true => {
noOfMessageRcvd +=1
if(noOfMessages == noOfMessageRcvd){
self ! "shutdown"
}
}
case "stats" => {
logStats()
}
case "shutdown" => {
logStats()
log.info("Shutting Down!")
context.system.shutdown()
}
}
def logStats(){
var duration = (System.currentTimeMillis - start)/1000
if( duration > 0) {
log.info(noOfMessageRcvd+" messages processed in "+duration +" seconds "
+ "at "+ noOfMessageRcvd/duration +" TPS" )
} else {
log.info(noOfMessageRcvd+" messages processed in less than a second ")
}
}
}
object RedLoad extends App{
val log = Logger.getLogger(this.getClass().getName())
val system = ActorSystem("LoadGeneratorApp");
// -1 is if we want to run for a period of time and > 1 the run will end after the messages are procesed
val lg = system.actorOf(Props(new LoadGenerator(100,-1)),"LG")
//Log the stats every 30 seconds
system.scheduler.schedule(0 seconds,30 seconds,lg,"stats")
//Shutdown the load run after 2 hours, if no of message is > -1 then it will shutdown after
//all messages are processed
system.scheduler.scheduleOnce(2 hours,lg,"shutdown")
lg ! "start"
log.info("Started..")
}
Well, your actor can't process more than one message at a time, and you just set it to be busy forever sending messages. Working as designed. Remove the endless loop and send batches of messages to the loadRouter and send yourself continuation-messages to keep sending more messages.
case SendBatch =>
(1 to batchSize) foreach { router ! message }
self ! SendBatch
I want to rewrite in scala the example from Sun's tutorial about concurrency in java. The original code is here: http://java.sun.com/docs/books/tutorial/essential/concurrency/deadlock.html
This code is incorrect. It freezes at where the comment indicates. Could anyone correct this? Thanks in advance.
import scala.actors.Actor
class Person(val name: String) extends Actor {
def bow(other: Person) {
other ! Bow(this)
}
private def bowBack(backTo: Person) {
println(this.name + " is bowing back to " + backTo.name)
backTo ! Bowed(this)
}
def act() {
while (true) {
receive {
case Bow(p) =>
println(this.name + " is bowing to " + p.name)
p ! BowBack(this)
println(" wating for bowing back...")
var received = false
while (true && received == false) {
receive { //blocked here
case Bowed(other) if p == other =>
println(" ... " + this.name + " has bowed to " + other.name)
received == true
}
}
println(this.name + " has bowed to " + p.name)
case BowBack(p) =>
println(this.name + " is bowing back to " + p.name)
p ! Bowed(this)
case "EXIT" => return
case x => println(x)
}
}
}
}
abstract case class Action()
case class Bow(person: Person) extends Action
case class BowBack(person: Person) extends Action
case class Bowed(person: Person) extends Action
object BowTest extends Application {
val a = new Person("Alphone")
val g = new Person("Gaston")
a.start()
g.start()
a ! Bow(g)
//g.bow(a)
a ! "EXIT"
g ! "EXIT"
}
The first mistake is that you wrote result == true. This should be changed to result = true
You should delete the true value from the while condition. It has no influence.
In the BowTest object you should add after the g.bow(a) instruction Thread.sleep(1000) to give actors enough time to respond to the messages.
In this way your code should work. But still it has a deadlock. If you will change g.bow(a) in a.bow(g) the execution will freeze. This is caused by the receive block. Each actor is waiting for the Bowed message, but they cannot respond to the BowBack message.
When you are responding to a message, you should use receive block only if you are sure that the actor will get the specified messages. But usually this is not a good practice in designing actors. They should not block. The main purpose of an actor is to respond as fast as possible to a message. If you have to do a big task you should use futures, but in this case is not required.
A solution will be to retain the persons which are bowed in a list. When the actor have to bow a person it adds it in the list. When the actor is bowed by a person which is in the list, it removes that person from the list.
while (true) {
react {
case Bow(p) =>
println(this.name + " is bowing to " + p.name)
addPersonToBowList(p)
p ! BowBack(this)
case Bowed(other) if isPersonInBowList(other) =>
println(" ... " + this.name + " has bowed to " + other.name)
removePersonFromBowList(other)
case BowBack(p) =>
println(this.name + " is bowing back to " + p.name)
p ! Bowed(this)
case "EXIT" => exit()
case x => println(x)
}
}
Not deadlock, just a plain ol' bug, I think. Two lines under your comment:
received == true
This should be = instead of ==. I've not looked in depth (this sprung out at me) but it looks like this would fix your issue.