tags:

views:

617

answers:

2

I don't get the actual (semantic) difference between the two "expressions".
It is said "loop" fits to "react" and "while(true)" to "receive", because "react" does not return and "loop" is a function which calls the body again all over again (it least this is what I deduct from the sources - I am not really familiar with the used "andThen"). "Receive" blocks one Thread from the pool, "react" does not. However, for "react" a Thread is looked up which the function can be attached to.

So the question is: why can't I use "loop" with "receive"? It also seems to behave different (and better!) than the "while(true)" variant, at least this is what I observe in a profiler. Even more strange is that calling a ping-pong with "-Dactors.maxPoolSize=1 -Dactors.corePoolSize=1" with "while(true)" and "receive" blocks immediately (that's what I would expect) - however, with "loop" and "receive", it works without problems - in one Thread - how's this?

Thanks!

+2  A: 

The method loop is defined in the object Actor:

private[actors] trait Body[a] {
  def andThen[b](other: => b): Unit
}

implicit def mkBody[a](body: => a) = new Body[a] {
  def andThen[b](other: => b): Unit = self.seq(body, other)
}

/**
 * Causes <code>self</code> to repeatedly execute
 * <code>body</code>.
 *
 * @param body the code block to be executed
 */
def loop(body: => Unit): Unit = body andThen loop(body)

This is confusing, but what happens is that the block that comes after loop (the thing between { and }) is passed to the method seq as first argument, and a new loop with that block is passed as the second argument.

As for the method seq, in the trait Actor, we find:

private def seq[a, b](first: => a, next: => b): Unit = {
  val s = Actor.self
  val killNext = s.kill
  s.kill = () => {
    s.kill = killNext

    // to avoid stack overflow:
    // instead of directly executing `next`,
    // schedule as continuation
    scheduleActor({ case _ => next }, 1)
    throw new SuspendActorException
  }
  first
  throw new KillActorException
}

So, the new loop is scheduled for the next action after a kill, then the block gets executed, and then an exception of type KillActorException is thrown, which will cause the loop to be executed again.

So, a while loop performs much faster that a loop, as it throws no exceptions, does no scheduling, etc. On the other hand, the scheduler gets the opportunity to schedule something else between two executions of a loop.

Daniel
Is it the last reason (last sentence) that makes it possible to use 2 different "receives" on only one Thread (due to the scheduling) with loop?
Ice09
Yes, but, then, react is faster than receive if you don't want to keep the thread to yourself.
Daniel
+2  A: 

The critical difference between while and loop is that while restricts the loop iterations to occur in the same thread. The loop construct (as described by Daniel) enables the actor sub-system to invoke the reactions on any thread it chooses.

Hence using a combination of receive within while (true) ties an actor to a single thread. Using loop and react allows you run support many actors on a single thread.

oxbow_lakes
Ok, so if I combine loop and receive (which is the combination in question), also receive might be executed in different threads? From your explanation I understand that the combination of while(t) and react would make no sense at all (due to the non-returning of react), but I don't quite get the implication of loop/react.
Ice09
I think if you look at the source of receive that it will block the current thread.
oxbow_lakes
(Which is in the `suspendActor` method and is called from `receive`)
oxbow_lakes
yes, that's true. However, it still could work together with "loop", even if it blocks the current thread. But, as Daniel commented, it just would not make sense to use it, because react is then just faster - but that seems to be the only reason. To my understanding, I could just use the "loop-pattern" for both (receive/react), having in mind that they behave quite different. But then again, while(true) should be faster since it throws no exceptions and does no scheduling. So, anyway, it seems to be clever to use the loop/react - while/receive pattern as advised...
Ice09
I wasn't saying anything other than `receive` blocks the current thread. Why would you use `receive` within a `loop` as it gives no extra functionality over `while (true)`? The whole point of `loop` is that it goes with `react` to achieve the same functionality in a scalable (from a concurrency perspective) manner
oxbow_lakes