4
votes

I was writing a little test program to try out some things with Remote Actors that I was going to need in a Scala project.

The basic goal was to write a test application of one server that could handle a bunch of clients and more important clients that can send multiple messages at the same time (like pings, requests for updates and user induced requests for data)

What I came up with was this: brief overview: the client starts 3 different actors which again start actors in while loops with different offsets in order to simulate rather random messages.

import scala.actors.remote.RemoteActor
import scala.actors.remote.Node
import scala.actors.Actor

trait Request
trait Response

case object WhoAmI extends Request
case class YouAre(s:String) extends Response

case object Ping extends Request
case object Pong extends Response

case class PrintThis(s:String) extends Request
case object PrintingDone extends Response

object Server {
  def main(args: Array[String]) {
    val server = new Server
    server.start
  }
}

class Server extends Actor {
  RemoteActor.alive(12345)
  RemoteActor.register('server, this)
  var count:Int = 0

  def act() {
    while(true) {
      receive {
        case WhoAmI => {
          count += 1
          sender ! YouAre(count.toString)
        }
        case Ping => sender ! Pong
        case PrintThis(s) => {
          println(s)
          sender ! PrintingDone
        }
        case x => println("Got a bad request: " + x)

      }
    }
  }
}

object Act3 extends scala.actors.Actor {
  def act = {
    var i = 0
    Thread.sleep(900)
    while (i <= 12) {
      i += 1
      val a = new Printer
      a.start
      Thread.sleep(900)
    }
  }
}

class Printer extends scala.actors.Actor {
  def act = {
    val server = RemoteActor.select(Node("localhost",12345), 'server)
    server ! PrintThis("gagagagagagagagagagagagaga")
    receive {
      case PrintingDone => println("yeah I printed")
      case _            => println("got something bad from printing")
    }
  }
}

object Act2 extends scala.actors.Actor {
  def act = {
    var i = 0

    while (i < 10) {
      i+=1
      val a = new Pinger
      a.start
      Thread.sleep(700)
    }
  }
}

class Pinger extends scala.actors.Actor {
  def act = {
    val server = RemoteActor.select(Node("localhost",12345), 'server)
    server ! Ping
    receive {
      case Pong => println("so I pinged and it fits")
      case x    => println("something wrong with ping. Got " + x)
    }
  }
}

object Act extends scala.actors.Actor {
  def act = {
    var i = 0

    while(i < 10) {
      i+=1
      val a = new SayHi
      a.start()
      Thread.sleep(200)
    }

  }
}

class SayHi extends scala.actors.Actor {
  def act = {
    val server = RemoteActor.select(Node("localhost",12345), 'server)
    server ! "Hey!"
  }
}

object Client {
  def main(args: Array[String]) {
    Act.start()
    //Act2.start()
    Act3.start()
  }
}

The problem is that things don't run as smoothly as I'd expect them to: when I start only one of the client actors (by commenting the others out as I did with Act2in Client) things usually but not always go well. If I start two or more actors, quite often the printouts appear in bulk (meaning: there's nothing happening at once and then the printouts appear rather fast). Also the client sometimes terminates and sometimes doesn't.

This may not be the biggest problems but they're enough to make me feel quite uncomfortable. I did a lot of reading on Actors and Remote Actors but I find the available info rather lacking.

Tried to add exit statements where ever it seemed fit. But that didn't help.

Has anybody got an idea what I'm doing wrong? Any general tricks here? Some dos and donts?

2
Related artima.com/shop/actors_in_scala (but might not fix your problem)oluies

2 Answers

3
votes

My guess is that your issues stem from blocking your actor's threads by using receive and Thread.sleep. Blocking operations consume threads in the actors' thread pool, which can prevent other actors from executing until new threads are added to the pool. This question may provide some additional insight.

You can use loop, loopWhile, react, and reactWithin to rewrite many of your actors to use non-blocking operations. For example

import scala.actors.TIMEOUT

object Act extends scala.actors.Actor {
   def act = {
      var i = 0
      loopWhile(i < 10) {
         reactWithin(200) { case TIMEOUT =>
            i+=1
            val a = new SayHi
            a.start()
         }
      }
   }
}

Of course, you can eliminate some boilerplate by writing your own control construct:

def doWithin(msec: Long)(f: => Unit) = reactWithin(msec) { case TIMEOUT => f }
def repeat(times: Int)(f: => Unit) = {
   var i = 0
   loopWhile(i < times) {
      f
      i+=1
   }
}

This would allow you to write

repeat(10) {
   doWithin(200) {
      (new SayHi).start
   }
}
1
votes

You may try Akka actors framework instead http://akkasource.org/