Skip to content

lazy val locking #2275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
baszo opened this issue Apr 19, 2017 · 8 comments
Closed

lazy val locking #2275

baszo opened this issue Apr 19, 2017 · 8 comments

Comments

@baszo
Copy link

baszo commented Apr 19, 2017

I wrote a comment on SIP-20. And I think problem is also with dotty because it is based on same concept.

I found a problem with implementation of lazy val. When one thread will initialize lazy val and other check that val is already initializing then call wait4Notification and set flag to 2. Then it is possibility that first thread finished initializing and checked that flag which is set to 2 and do notifyAll on monitor. After that second thread will call wait and it will lock forever. Problem is with time between setting flag and wait on monitor.

My solution is :

@inline def wait4Notification(t: Object, offset: Long, cur: Long, ord: Int) = {
  println(s"${Thread.currentThread().getId()}-wait4Notification($t, $offset, $cur, $ord)")
  var retry = true
  while (retry) {
    val cur = get(t, offset)
    val state = STATE(cur, ord)
    if (state == 1) {
      val monitor = getMonitor(t, ord)
      //make sure that thread will wait on monitor
      monitor.synchronized {
        if(CAS(t, offset, cur, 2, ord))
          monitor.wait()
      }
    }
    else if (state == 2) {
      val monitor = getMonitor(t, ord)
      monitor.synchronized {
        println(s"${Thread.currentThread().getId} -$monitor - wait")
        monitor.wait()
      }
    }
    else retry = false
  }
}
@DarkDimius
Copy link
Contributor

Hi Andrzej,

Thanks for writing. You are right that the implementation has a formal bug. Strange that we haven't seen it ever occur in hours and hours of benchmarking the implementation both done by me and Alex.

Unfortunately your fix is both inefficient and incorrect:

  • inefficient in making CAS inside the first synchchronized block. it's a very pessimistic act that is rarely needed;
  • incorrect in that the second synchronized block still has the same issue and can lead to a thread waiting forever.

Could you please come to #2276 and review my proposed fix?

Thank you,
Dmitry

@DarkDimius
Copy link
Contributor

// I've modified your comment to fix github formatting.

@nicolasstucki
Copy link
Contributor

What is the status of this fix?

@Blaisorblade
Copy link
Contributor

The issue is still there, see #2276 (and #2535).

@smarter
Copy link
Member

smarter commented Jul 5, 2018

I think I just hit this for the first time, I was running our test suite but it got stuck at the very first test in dotty.tools.dotc.FromTastyTests.runTestFromTasty, see https://gist.github.com/smarter/588b5dcb19f26f7069c1f2b393995a56 for the jstack output.

@smarter
Copy link
Member

smarter commented Nov 12, 2018

Just hit this again in dotty.tools.io.FileZipArchive.$1$ while compiling https://github.com/PDBP/pdbp. I'm raising the priority of this because this is really a critical bug we need to fix somehow.

@allanrenucci
Copy link
Contributor

It seems I can reliably reproduce the issue with the following program:

object Test {
  var count = 0

  @volatile lazy val x: Int = {
    if (count < 100) {
      count += 1
      ???
    }
    1
  }

  def main(args: Array[String]): Unit = {
    def fetchLazy(): Unit = try x catch { case _: Throwable => fetchLazy() }

    for (_ <- 0 until 10) {
      new Thread(() => fetchLazy()).start()
    }
  }
}

@allanrenucci
Copy link
Contributor

Since we have concerns about the size of the generated code and the correctness of the new implementation, I propose we revert to the Scala 2 implementation for thread safe lazy fields. One can always revive SIP-20 in the future.

allanrenucci added a commit that referenced this issue Nov 15, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants