If I run a Scala repl (2.12.7), invoke :paste and paste that block, I never see the Future thread: println and the while loops forever.
But, if I paste everything except the last line (test()) and instead invoke it in the next repl prompt, everything works as expected.
This does not happen on a 2.11 repl.
I tried a few variations of this (all pasted in :paste mode):
I put that entire code block above inside an object, i.e. object Foo { .. block .. }
The behavior in the repl was interesting, it printed defined object Foo, then writing Foo in the prompt yielded the same behavior as before (loop forever)
Again I created an object Foo and put the test function in it, but did not invoke it, instead I put the invocation of test() inside a def main(args: Array[String]): Unit = test(). Then in the next repl prompt I wrote Foo.main(Array.empty) and everything worked as expected.
fwiw, this does not seem to be limited to the scala repl. I had this variation too:
And I had Intellij run Goo.main to see the same behavior.
I would love to hear people’s thoughts on this, or to actually figure out what’s going on? It seems like some kind of regression, and definitely not expected behavior, but I’m aware there might be aspects I’m not familiar with.
Interesting find. My first thought is that it’s because you didn’t use @transient on your boolean, but that’s not it. AtomicBoolean doesn’t help either.
I found that it’s broken without sleep inside the Future.
Here’s a variation I came up with while playing with your code.
I don’t see how the boolean continue has anything to do with this. The Future thread: line should be printed regardless of its value.
Perhaps this is a problem with streaming the output to the console / stdout? Try writing the text into a file instead of printing it and see if the file is still missing that line.
I only looked at desugared intermediate code and not the actual bytecode, but it seems to me like Foo’s instance is constructed while holding a lock. All the lambdas used inside Foo are lifted to methods on Foo’s instance and accessed by trying to acquire that lock again, in a different thread, so it deadlocks.
2.11 lambdas were top level classes instead and didn’t have this problem.
Makes sense – it seemed like there had to be something like that going on, preventing the Future from firing.
So the larger question raised by this is, is this a bug, or something to be documented, or what? The implication is that blocking during object construction is Dangerous. That doesn’t totally surprise me (it actually had never occurred to me to try this), and my reaction to the Foo version is that that’s a weird edge case that might just deserve a “don’t do that”. (Although I’m concerned about whether there might be other ways to hit this deadlock with interdependent closures and vars, without even getting into Futures and blocking.)
The REPL version is arguably a hair more serious – while it’s slightly weird code, there’s nothing obviously broken about it – and I assume is failing because it turns into something like the Foo version when you :paste it.
It feels like there’s a bug to be opened here, but offhand I’m not sure on what…
It’s not just limited to the REPL, though that’s where folks most frequently run in to it. The original issue came up in the context of ScalaCheck - https://github.com/rickynils/scalacheck/issues/290.