2

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:

enter image description here

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:

run 1 code in object constructor 1 run2 enter image description here

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

polo
  • 1,352
  • 2
  • 16
  • 35
  • 2
    There are a few scala tickets that boil down to don't start threads in static initializers. https://github.com/scala/bug/issues/9824 – som-snytt Dec 20 '18 at 17:29
  • 1
    After posting this I found my answer at this answer by @axel22: https://stackoverflow.com/questions/15176199/scala-parallel-collection-in-object-initializer-causes-a-program-to-hang/15176433#15176433 – polo Dec 20 '18 at 17:57

0 Answers0