Skip to content
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

2.12.1 dead-locks on some nasty nested Runnables #10169

Closed
scabug opened this issue Feb 1, 2017 · 5 comments
Closed

2.12.1 dead-locks on some nasty nested Runnables #10169

scabug opened this issue Feb 1, 2017 · 5 comments

Comments

@scabug
Copy link

scabug commented Feb 1, 2017

This was discovered in Finagle when compiled with Scala 2.12 (see twitter/finagle#581). Turns out some of the threads in the Netty worker pool dead-lock during the initialization with Scala 2.12.1 but work fine with 2.11.7.

I tried to simplify the reproducer as much as possible but I want to apologize for its size. It won't probably fit the Jira ticket so I created a self-contained Github repo (see https://github.com/vkostyukov/scala-2-12-1-and-runnable-bug) containing the reproducer.

Here are steps to reproduce the issue:

  1. git clone git@github.com:vkostyukov/scala-2-12-1-and-runnable-bug.git
  2. cd scala-2-12-1-and-runnable-bug
  3. sbt ++2.11.7 run (program terminates)
  4. sbt ++2.12.1 run (program never terminates)

I'm happy to provide more details if needed. My current hand-waving hypothesis is that it's somewhat a combination of (a) weirdly nested runnables used (b) during the class initialization.

@scabug
Copy link
Author

scabug commented Feb 1, 2017

Imported From: https://issues.scala-lang.org/browse/SI-10169?orig=1
Reporter: Vladimir Kostyukov (vkostyukov)
Affected Versions: 2.12.1
Duplicates #8119

@scabug
Copy link
Author

scabug commented Feb 1, 2017

@som-snytt said:
I can't resist such a nicely detailed ticket with proper use of its and it's. Pinch me if I'm dreaming.

It works if foo.Pool is lazy. I wonder if all value members of package objects should be implicitly lazy.

@scabug
Copy link
Author

scabug commented Feb 2, 2017

@som-snytt said:
In general, it's desirable to avoid starting threads from static initializers (such as an object constructor) because of the risk of deadlock during class loading.

The risk increases in 2.12 with lambdas. Besides making Pool a lazy val, the following change also unblocks, but it's really no way to live:

--- a/src/main/scala/foo/package.scala
+++ b/src/main/scala/foo/package.scala
@@ -7,6 +7,13 @@ package object foo {
 
   import bar.Proxy
 
+  val Pre = new Function0[Unit] {
+    def apply() = println("pre")
+  }   
+  val Post = new Function0[Unit] {
+    def apply() = println("post")
+  }
+
   val Executor: ExecutorService = {
     val threadFactory = new Proxy(
       new ThreadFactory {
@@ -17,12 +24,15 @@ package object foo {
         }
       },
       Proxy.newProxy(
+        Pre, Post
+        /*
         () => println("pre"),
         () => println("post")
+        */
       )
     )
     Executors.newCachedThreadPool(threadFactory)
   }
 
   val Pool: WorkerPool = new WorkerPool(Executor)
}

@scabug
Copy link
Author

scabug commented Feb 2, 2017

@som-snytt said:
Linking to the canonical ticket.

@scabug
Copy link
Author

scabug commented Feb 2, 2017

Vladimir Kostyukov (vkostyukov) said:
Thanks a lot! This is the conclusion I came up with - just remove those inlined anonymous functions from the class initialization path (see twitter/finagle@225c86d).

@scabug scabug closed this as completed Feb 4, 2017
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

1 participant