Recursive methods and react

You might be concerned that calling a recursive method as Listing 5.5 does could quickly lead to a stack overflow. The good news, however, is that react plays extremely well with recursive methods: whenever an invocation of react resumes due to a matching message in the actor's mailbox, a task item is created and submitted to the actor runtime's internal thread pool for execution. The thread that executes that task item doesn't have much else on its call stack, apart from the basic logic of being a pool worker thread. As a result, every invocation of react executes on a call stack that is as good as empty. The call stack of a recursive method like waitFor in Listing 5.5, therefore, doesn't grow at all thanks to the react invocations.

Composing react-based code with combinators

Sometimes it is difficult or impossible to use recursive methods for sequencing multiple reacts, which is the case when reusing classes and methods that use react. By their nature, reusable components cannot be changed after they have been built. In particular, we cannot simply perform invasive changes, such as when we added iteration through a recursive method in the example in Listing 5.5. This section illustrates several ways in which we can reuse react-based code.

    def sleep(delay: Long) {
      register(timer, delay, self)
      react { case 'Awake => /* OK, continue */ }
    }
Listing 5.6 - A sleep method that uses react.

For example, suppose our project contains the sleep method shown in Listing 5.6. It registers the current actor, self, with a timer service (not shown) to wake it up after the specified delay, which is provided as a parameter. The timer notifies the registered actor using an 'Awake message. For efficiency, sleep uses react to wait for the 'Awake so that the sleeping actor does not require the resources of a JVM thread while it is sleeping.

Using the sleep method shown in Listing 5.6 invariably requires executing something after its react invocation. However, since we want to reuse the method as is, we cannot simply insert something in the body of its react. Instead, we need a way to combine the sleep method with the code that should run after the 'Awake message has been received without changing the implementation of sleep.

That is where the Actor object's control-flow combinators come into play. These combinators let you express common communication patterns in a relatively simple and concise way. The most basic combinator is andThen. The andThen combinator combines two code blocks to run after each other even if the first one invokes react.

Listing 5.7 shows how you can use andThen to execute code that runs after invoking the sleep method. You use andThen as an operator that is written infix between two blocks of code. The first block of code invokes sleep as its last action, which, in turn, invokes react.

    actor {
      val period = 1000
      {
        // code before going to sleep
        sleep(period)
      } andThen {
        // code after waking up
      }
    }
Listing 5.7 - Using andThen to continue after react.

Note that the period parameter of sleep is declared outside the code block on which andThen operates. This is possible because the two code blocks are actually closures that may capture variables in their environment. The second block of code is run when the react of the first code block (the one inside sleep) is finished. However, note that the second code block is really the last thing the actor executes. Using andThen does not change the fact that react invocations do not return. andThen merely allows you to combine two pieces of code in sequence.

Another useful combinator is loopWhile. As its name suggests, it loops running a provided closure while some condition holds. Thanks to Scala's flexible syntax, loopWhile feels almost like a native language primitive. Listing 5.8 shows a variation of our actor chain example that uses loopWhile to wait for multiple 'Die messages. Again, we make use of the fact that the two code block parameters of loopWhile, the condition (n > 0) and the body, are closures, since both code blocks access the local variable n. (Each actor instance will wait for lives 'Die events before actually giving up the ghost.) Note that the top-level react in the body of loopWhile is unchanged from the first example that did not support iteration. You might as well extract the body to a method—loopWhile works in either case.

  def buildChain(size: Int, next: Actor, lives: Int): Actor = {
    val a = actor {
      var n = lives
      loopWhile (n > 0) {
        n -= 1
        react {
          case 'Die =>
            val from = sender
            if (next != null) {
              next ! 'Die
              react { case 'Ack => from ! 'Ack }
            } else from ! 'Ack
        }
      }
    }
    if (size > 0) buildChain(size - 1, a, lives)
    else a
  }
Listing 5.8 - Using loopWhile for iterations with react.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset