I'm trying to understand a difference in the correctness of this concurrent scala code between the code running in a class or an object method vs in an object constructor. I am wondering if anyone can help me explain this behaviour.
I'm running this MCVE example code:
val names: Seq[String] = Seq("foo1", "bar1", "foo2", "bar2")
var i=0
names.grouped(2).toList.map { groupedNames => {
i = i + 1
println(s"[${i}] - [groupedNames=${groupedNames.size}=${groupedNames}]")
val tasks: Seq[Future[Unit]] = for (name <- groupedNames) yield Future {
println(s"[${i}] - [name=${name}]")
}
val aggregated: Future[Seq[Unit]] = Future.sequence(tasks)
Await.result(aggregated, Duration.Inf)
}}
When I run it in a class constructor, a class method or an object method it works as expected. For example:
import scala.concurrent.duration._
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
object bar {
def foo = {
val names: Seq[String] = Seq("foo1", "bar1", "foo2", "bar2")
var i=0
names.grouped(2).toList.map { groupedNames => {
i = i + 1
println(s"[${i}] - [groupedNames=${groupedNames.size}=${groupedNames}]")
val tasks: Seq[Future[Unit]] = for (name <- groupedNames) yield Future {
println(s"[${i}] - [name=${name}]")
}
val aggregated: Future[Seq[Unit]] = Future.sequence(tasks)
Await.result(aggregated, Duration.Inf)
}}
}
}
object app extends App {
println("Press any key to continue")
scala.io.StdIn.readLine()
bar.foo
println("And again press any key to continue")
scala.io.StdIn.readLine()
}
When I run it I get:
$> scala app
Press any key to continue
[1] - [groupedNames=2=List(foo1, bar1)]
[1] - [name=foo1]
[1] - [name=bar1]
[2] - [groupedNames=2=List(foo2, bar2)]
[2] - [name=foo2]
[2] - [name=bar2]
And again press any key to continue
and this is what a graph of the threads:
but when I run it in the bar
object constructor (not wrapped in a method) I consistently get a deadlock:
import scala.concurrent.duration._
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
object bar {
//def foo = {
val names: Seq[String] = Seq("foo1", "bar1", "foo2", "bar2")
var i=0
names.grouped(2).toList.map { groupedNames => {
i = i + 1
println(s"[${i}] - [groupedNames=${groupedNames.size}=${groupedNames}]")
val tasks: Seq[Future[Unit]] = for (name <- groupedNames) yield Future {
println(s"[${i}] - [name=${name}]")
}
val aggregated: Future[Seq[Unit]] = Future.sequence(tasks)
Await.result(aggregated, Duration.Inf)
}}
//}
}
object app extends App {
println("Press any key to continue")
scala.io.StdIn.readLine()
bar
println("And again press any key to continue")
scala.io.StdIn.readLine()
}
and run:
$> scala app
Press any key to continue
[1] - [groupedNames=2=List(foo1, bar1)]
Looking at the threads when the code is part of the object constructor the main thread consistently deadlocks:
It's worth repeating that I only get this deadlock in an object constructor. When I put the same code in a class constructor or an object of class method, I don't get a deadlock