0

I have a method where I tried to parallelise the calc using GPARS and calculate an aggregate boolean 'And' result across the calls. This method is wrapped as a @ActiveObject which will deliver the result as a dataflow - the code below has the original approach where I tried to store the aggregate using AtomicBoolean to protect it.

This didn't work (sometimes my tests would pass others they would fail) on the calculated 'end truth'. To fix this I changed from AtomicBoolean to a Agent(boolean) approach and I think it's 'fixed' it - at least my spock tests are continuously succeeding.

Where was my logic flawed trying to use AtomicBoolean to build the final result? It felt like it should work - but doesn't and I don't understand why.

Method below - I've put the original version, and the corrected version below

@ActiveMethod
def evaluateAllAsync () {
    AtomicBoolean result = new AtomicBoolean(true)
    GParsPool.withPool {
        // do as parallel 
        conditions.eachParallel { condition ->
            println "evalAllAsync-parallel intermediate result start value is ${result.get()} and condition with expression ${condition.expression} evaluated to ${condition.evaluate()}"
            result.getAndSet(result.get() && condition.evaluate())
            println "recalc bool value is now ${result.get()}"
        }
    }
    println "evalAllAsync-parallel final result value is ${result.get()}"
    result.get()

}

Fixed issue by using Agent form like this

@ActiveMethod
def evaluateAllAsync () {
    def result = new Agent (true)
    GParsPool.withPool {
        // do as parallel 
        conditions.eachParallel { condition ->
            println "evalAllAsync-parallel intermediate result start value is ${result.val} and condition with expression ${condition.expression} evaluated to ${condition.evaluate()}"
            result << { def res = it && condition.evaluate(); println "start> $it and finish> $res"; updateValue(res)}
            println "recalc bool value is now ${result.val}"
        }
    }
    println "evalAllAsync-parallel final result value is ${result.val}"
    result.val

}

I put debug println in here just so I could see what code was doing.

The version with the Agent to protect the bool aggregate value appears to be working.

Why doesn't the Atomic Boolean work?

halfer
  • 19,824
  • 17
  • 99
  • 186
WILLIAM WOODMAN
  • 1,185
  • 5
  • 19
  • 36

1 Answers1

0

Well, this is an issue in how you use AtomicBoolean. You always overwrite by force (getAndSet()) the value stored in it and ignore the possibility that other threads could have changed it while the current thread is busy 'evaluating'.

You perhaps wanted to use the compareAndSet() method instead:

    def valueToUse = result.get()
    result.compareAndSet(valueToUse, valueToUse && condition.evaluate())
Vaclav Pech
  • 1,431
  • 8
  • 6
  • i think thats because i was assuming that the getAndSet itself is atomic and so will do its update without interference from other threads. the atomic.get Inside is guaranteed - so then && with the evaluation, then set that back, so i think its my assumption about how these two would work (evaluate the get&&eval first - then present that to the getAndSet - assuming that the next get would see any corrected values. – WILLIAM WOODMAN Dec 02 '16 at 15:30
  • however thats clearly not whats happening - when i watched my console print the all start out the get returning true, the evalauated get && eval prints out the true/false as expected but the scheduled update and final return sometimes shows true, sometimes false - as this was happeing in the with pool closure - i expected the final get to see the correct final calc – WILLIAM WOODMAN Dec 02 '16 at 15:33
  • so my understanding of the non blocking and how the race condition is happening is not right - maybe as you say i'm just overwriting a earlier update, so if send update went false, but was replaced with 3rd update calc as true - then i'm just overwriting - not controlling how the next thread starts with the right value. its telling that the console says they all start as true. – WILLIAM WOODMAN Dec 02 '16 at 15:36
  • that kind of implies they all went and read very quickly - all saw the same value, but the reeval calc takes time - so the final updates just randomly update the value and is then overwriiten by the next thread - so they have missed the updates from previous thread. i'll try and change a sample to use compare and set and come back – WILLIAM WOODMAN Dec 02 '16 at 15:39
  • think this probably describes my misunderstanding and your suggested resolution . http://stackoverflow.com/questions/28147654/difference-between-getandset-and-compareandset-in-atomicboolean – WILLIAM WOODMAN Dec 02 '16 at 15:43