My question is probably vague (could not think of how to describe it well) but hopefully this example will make things more clear:
class IntTestFake extends FunSpec with ScalaFutures {
describe("This"){
it("Fails for some reason"){
var a = "Chicken"
val b = "Steak"
def timeout() = Future{
while(a != b){}
}
Future{
Thread.sleep(3000)
a = b
}
whenReady(timeout(), Timeout(20 seconds), Interval(50 milliseconds))(result => result)
}
it("Passes...why?!?"){
var a = "Chicken"
val b = "Steak"
def timeout() = Future{
while(a != b){
println("this works...")
}
}
Future{
Thread.sleep(3000)
a = b
}
whenReady(timeout(), Timeout(20 seconds), Interval(50 milliseconds))(result => result)
}
}
}
In the first test (Fails for some reason
) the while loop has an empty body. In the second test (Passes...why?!?
) the while loop body has a println statement in it. My original thought was garbage collection was doing something funky but with that whenReady
statement I am expecting something to return so I would expect GC to leave it alone until then. Apologies if this has already been asked I could not find an example.
a
needs to be @volatile
, without it writes from other threads are not guaranteed to be visible to the current thread, until it hits a "memory barrier" (a special point in the code, where all caches are flashed - in a conceptual sense as pointed out in the comment, not necessarily mapping directly to how exactly hardware off a particular cpu handles that). This is why the second case works - there's plenty of memory barriers inside a println
call.
So, changing var a ...
to @volatile var a ...
will make it work ... but, seriously, don't use vars
. At least, not until you have learned enough scala to be able to recognize the cases where you have to have them.