(Quick Reference)

5.3 Tips and Tricks - Reference Documentation

Authors: The Whole GPars Gang

Version: 1.1.0

5.3 Tips and Tricks

Structuring actor's code

When extending the DefaultActor class, you can call any actor's methods from within the act() method and use the react() or loop() methods in them.
class MyDemoActor extends DefaultActor {

protected void act() { handleA() }

private void handleA() { react {a -> handleB(a) } }

private void handleB(int a) { react {b -> println a + b reply a + b } } }

final def demoActor = new MyDemoActor() demoActor.start()

Actors.actor { demoActor 10 demoActor 20 react { println "Result: $it" } }.join()

Bear in mind that the methods handleA() and handleB() in all our examples will only schedule the supplied message handlers to run as continuations of the current calculation in reaction to the next message arriving.

Alternatively, when using the actor() factory method, you can add event-handling code through the meta class as closures.

Actor demoActor = Actors.actor {
    delegate.metaClass {
        handleA = {->
            react {a ->
                 handleB(a)
            }
        }

handleB = {a -> react {b -> println a + b reply a + b } } }

handleA() }

Actors.actor { demoActor 10 demoActor 20 react { println "Result: $it" } }.join()

Closures, which have the actor set as their delegate can also be used to structure event-handling code.

Closure handleB = {a ->
    react {b ->
        println a + b
        reply a + b
    }
}

Closure handleA = {-> react {a -> handleB(a) } }

Actor demoActor = Actors.actor { handleA.delegate = delegate handleB.delegate = delegate

handleA() }

Actors.actor { demoActor 10 demoActor 20 react { println "Result: $it" } }.join()

Event-driven loops

When coding event-driven actors you have to have in mind that calls to react() and loop() methods have slightly different semantics. This becomes a bit of a challenge once you try to implement any types of loops in your actors. On the other hand, if you leverage the fact that react() only schedules a continuation and returns, you may call methods recursively without fear to fill up the stack. Look at the examples below, which respectively use the three described techniques for structuring actor's code.

A subclass of DefaultActor

class MyLoopActor extends DefaultActor {

protected void act() { outerLoop() }

private void outerLoop() { react {a -> println 'Outer: ' + a if (a != 0) innerLoop() else println 'Done' } }

private void innerLoop() { react {b -> println 'Inner ' + b if (b == 0) outerLoop() else innerLoop() } } }

final def actor = new MyLoopActor().start() actor 10 actor 20 actor 0 actor 0 actor.join()

Enhancing the actor's metaClass

Actor actor = Actors.actor {

delegate.metaClass { outerLoop = {-> react {a -> println 'Outer: ' + a if (a!=0) innerLoop() else println 'Done' } }

innerLoop = {-> react {b -> println 'Inner ' + b if (b==0) outerLoop() else innerLoop() } } }

outerLoop() }

actor 10 actor 20 actor 0 actor 0 actor.join()

Using Groovy closures

Closure innerLoop

Closure outerLoop = {-> react {a -> println 'Outer: ' + a if (a!=0) innerLoop() else println 'Done' } }

innerLoop = {-> react {b -> println 'Inner ' + b if (b==0) outerLoop() else innerLoop() } }

Actor actor = Actors.actor { outerLoop.delegate = delegate innerLoop.delegate = delegate

outerLoop() }

actor 10 actor 20 actor 0 actor 0 actor.join()

Plus don't forget about the possibility to use the actor's loop() method to create a loop that runs until the actor terminates.

class MyLoopingActor extends DefaultActor {

protected void act() { loop { outerLoop() } }

private void outerLoop() { react {a -> println 'Outer: ' + a if (a!=0) innerLoop() else println 'Done for now, but will loop again' } }

private void innerLoop() { react {b -> println 'Inner ' + b if (b == 0) outerLoop() else innerLoop() } } }

final def actor = new MyLoopingActor().start() actor 10 actor 20 actor 0 actor 0 actor 10 actor.stop() actor.join()