-
Notifications
You must be signed in to change notification settings - Fork 29k
[SPARK-51821][CORE] Call interrupt() without holding uninterruptibleLock to avoid possible deadlock #50594
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
[SPARK-51821][CORE] Call interrupt() without holding uninterruptibleLock to avoid possible deadlock #50594
Changes from all commits
8485598
bebf0cb
d0be1a3
b7e6493
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
|
|
||
| package org.apache.spark.util | ||
|
|
||
| import java.nio.channels.spi.AbstractInterruptibleChannel | ||
| import java.util.concurrent.{CountDownLatch, TimeUnit} | ||
|
|
||
| import scala.util.Random | ||
|
|
@@ -115,6 +116,45 @@ class UninterruptibleThreadSuite extends SparkFunSuite { | |
| assert(interruptStatusBeforeExit) | ||
| } | ||
|
|
||
| test("no runUninterruptibly") { | ||
| @volatile var hasInterruptedException = false | ||
| val t = new UninterruptibleThread("test") { | ||
| override def run(): Unit = { | ||
| if (sleep(0)) { | ||
| hasInterruptedException = true | ||
| } | ||
| } | ||
| } | ||
| t.interrupt() | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks that Java 8 behaves differently when interrupt() is called on not started thread. |
||
| t.start() | ||
| t.join() | ||
| assert(hasInterruptedException === true) | ||
| } | ||
|
|
||
| test("SPARK-51821 uninterruptibleLock deadlock") { | ||
| val latch = new CountDownLatch(1) | ||
| val task = new UninterruptibleThread("task thread") { | ||
| override def run(): Unit = { | ||
| val channel = new AbstractInterruptibleChannel() { | ||
| override def implCloseChannel(): Unit = { | ||
| begin() | ||
| latch.countDown() | ||
| try { | ||
| Thread.sleep(Long.MaxValue) | ||
| } catch { | ||
| case _: InterruptedException => Thread.currentThread().interrupt() | ||
| } | ||
| } | ||
| } | ||
| channel.close() | ||
|
Comment on lines
+138
to
+149
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't seem to be a right use case of override def run(): Unit = {
this.runUninterruptibly {
...
}
}The task thread can be correctly interrupted if the whole block run inside
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see where the problem is. Spark task always uses the UninterruptibleThread but
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Streaming also appears to use
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think we should avoid using
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mridulm @Ngone51 Streaming requires |
||
| } | ||
| } | ||
| task.start() | ||
| assert(latch.await(10, TimeUnit.SECONDS), "await timeout") | ||
| task.interrupt() | ||
vrozov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| task.join() | ||
| } | ||
|
|
||
| test("stress test") { | ||
| @volatile var hasInterruptedException = false | ||
| val t = new UninterruptibleThread("test") { | ||
|
|
@@ -148,9 +188,20 @@ class UninterruptibleThreadSuite extends SparkFunSuite { | |
| } | ||
| } | ||
| t.start() | ||
| for (i <- 0 until 400) { | ||
| Thread.sleep(Random.nextInt(10)) | ||
| t.interrupt() | ||
| val threads = new Array[Thread](10) | ||
| for (j <- 0 until 10) { | ||
| threads(j) = new Thread() { | ||
| override def run(): Unit = { | ||
| for (i <- 0 until 400) { | ||
| Thread.sleep(Random.nextInt(10)) | ||
| t.interrupt() | ||
| } | ||
| } | ||
| } | ||
| threads(j).start() | ||
| } | ||
| for (j <- 0 until 10) { | ||
| threads(j).join() | ||
| } | ||
| t.join() | ||
| assert(hasInterruptedException === false) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.