Chained lazy vals can cause deadlocks

251 views
Skip to first unread message

Scott Morrison

unread,
May 19, 2012, 2:29:35 AM5/19/12
to scala...@googlegroups.com
Is it expected that this should deadlock? Is it a known bug/feature? Assuming the former, I created https://issues.scala-lang.org/browse/SI-5808.

object LazyValDeadLock extends App {
  lazy val X = 0
  lazy val Y = {
    // 'mentioning' X here solves the problem:
    // X
    for (i <- 0 until 2 par) yield  {
      println(i)
      X
    }
  }
  println(Y)
}

Anyone know what's going on?

best regards,
Scott Morrison

√iktor Ҡlang

unread,
May 19, 2012, 6:29:16 AM5/19/12
to Scott Morrison, scala...@googlegroups.com
lazy val synchronizes on this to ensure it's only initialized once, and that all concurrent consumers wait for intitialization is done.

Since parallel collections block until result is computed your program is hosed.

Cheers,
 

best regards,
Scott Morrison



--
Viktor Klang

Akka Tech Lead
Typesafe - The software stack for applications that scale

Twitter: @viktorklang

Scott Morrison

unread,
May 21, 2012, 3:31:13 AM5/21/12
to scala...@googlegroups.com, Scott Morrison
Hi Victor,

I don't understand your answer yet; could you explain what you mean by "and that all concurrent consumers wait for intitialization is done".

Note that the following also deadlocks (making it clear that println(Y) in the initialization block is not relevant):

-------
object LazyValDeadLock extends App {
  println((new Z).Y)
}

class Z {
  lazy val X = 0
  lazy val Y = {
    // 'mentioning' X here solves the problem:
    // X
    for (i <- 0 until 2 par) yield {
      println(i)
      X
    }
  }
}
-------

√iktor Ҡlang

unread,
May 21, 2012, 4:03:06 AM5/21/12
to Scott Morrison, scala...@googlegroups.com
On Mon, May 21, 2012 at 9:31 AM, Scott Morrison <scott.m...@gmail.com> wrote:
Hi Victor,

I don't understand your answer yet; could you explain what you mean by "and that all concurrent consumers wait for intitialization is done".

when one thread initializes Y (in your case the Main Thread) it takes the lock on the instance of Z, then you spawn other threads in "par" to execute the "yield", they try to read X, but for X to initialize it needs the lock on the Z instance, which is still held by the Main Thread.

Case closed

Cheers,
Reply all
Reply to author
Forward
0 new messages