2
votes

Groovy: 1.8.6

GPars: 0.12 or 1.2.1

OS: Ubuntu 14.04 LTS

@Grab(group='org.codehaus.gpars', module='gpars', version='0.12')
import groovyx.gpars.actor.Actor


import groovyx.gpars.actor.Actors


def a = 1
def b = 100000

def reactor2 = Actors.reactor {
    println " $it"
}

def reactor1 = Actors.reactor {
    println "$it"
    reactor2 << it
}

Actor actor = Actors.actor {
    (a..b).each {reactor1 << it}
}

actor.join()

reactor1.stop()
reactor1.join()

reactor2.stop()
reactor2.join()

When this code is executed, NullPointerException is occurred often. Wider and wider the range of a and b is, more and more easily this error is occurred. But the error is never occurred when the range is limited.

I cannot understand why the error is occurred.

1
Can you put the exception into your question?tim_yates
Also, can you use a version of groovy that's newer than 3 years old?tim_yates

1 Answers

0
votes

From the code example you've provided, I'm not 100% sure what you are trying to accomplish. It appears you are trying to an Actor for each value within the range provided and react to the message by printing out the value provided.

Given that, your code has some issues. The main issue is the lack of the loop{} closure, which insures the Actor waits for the next incoming message after processing one.

Secondly, invoking stop() doesn't help either. It simply stops the Actor from receiving additional messages. It doesn't hurt anything in your case, since you immediately call join, but adds to the confusion.

To get this working, here is your code simplified into a working example:

import groovyx.gpars.actor.Actors

def a = 1
def b = 100000

def actor = Actors.actor {
    loop {
       react {
            println it
        }
    }
}

(a..b).each {
    actor << it
}
actor.join()

In this example, for each value in the range, a message is added to the actor, which reacts to the message by printing the value and then, because of the 'loop' closure waits for the next incoming message.

So that example should accomplish what you want. However, to give some clarity into what is going on in your code, here is an explanation.

When your Actor 'reacts' to a message, you are sending a message to a Reactor. Even though you don't explicitly return a value, in Groovy, the last line within a Closure is the return statement. As a result, since your last line within the Closure is a println, the return of that is null. So, your Reactor returns null, which is treated as a message to the Actorand again processed.

To avoid this scenario, you need to evaluate the return message and only delegate the message / or print it if it has not yet been processed. I have updated your code, intentionally return 'done' as the return message for clarity. You could change the code to just check for a null message before processing it:

import groovyx.gpars.actor.Actors

def a = 1
def b = 100000

def reactor2 = Actors.reactor { message ->
    if(!message.equals("done")) {
        println "\t\tReact Again: $message"
    }
    return "done"
}

def reactor = Actors.reactor { message ->
    if(!message.equals("done")) {
        println "\tReact: $message"
        reactor2 << message
    }
    return "done"
}

def actor = Actors.actor {
    loop {
        react {
            if(!it.equals("done")) {
                println it
                reactor << it
            }
        }
    }
}

(a..b).each {
    actor << it
    // actor.oi
}
actor.join()