Search code examples
scalawhile-loopfuturescalatest

Empty while loop in Future causes Future to never return in Scala


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.


Solution

  • 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.