-
Notifications
You must be signed in to change notification settings - Fork 28.1k
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-9026] Refactor SimpleFutureAction.onComplete to not launch separate thread for every callback #7385
Closed
JoshRosen
wants to merge
16
commits into
apache:master
from
JoshRosen:simplefutureaction-refactoring
Closed
[SPARK-9026] Refactor SimpleFutureAction.onComplete to not launch separate thread for every callback #7385
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
df20ed5
Add regression test
JoshRosen 55c41d3
Refactor SimpleFutureAction to not block threads for every onComplete…
JoshRosen 1deed38
Add some comments
JoshRosen d779af8
Back out log4j.properties changes.
JoshRosen 1e2db7f
Fix race.
JoshRosen 1346313
Use lazy val to make ot clear that resultfunc should only be evaluate…
JoshRosen e08623a
Convert JobWaiter into a Future
JoshRosen b504384
Remove unnecessary notifyAll() cals.
JoshRosen 12ddad6
Remove unnecessary test
JoshRosen dae8805
Use success instead of trySuccess; etc.
JoshRosen c6fdc21
Merge remote-tracking branch 'josh/simplefutureaction-refactoring' in…
JoshRosen 7b22514
Merge remote-tracking branch 'origin/master' into simplefutureaction-…
JoshRosen 1a19268
Make jobFailed idempotent
JoshRosen c9ef8d4
Merge remote-tracking branch 'origin/master' into simplefutureaction-…
JoshRosen 692b3a4
Merge remote-tracking branch 'origin/master' into simplefutureaction-…
JoshRosen 17edbcd
Exception -> Throwable
JoshRosen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,10 @@ | |
|
||
package org.apache.spark.scheduler | ||
|
||
import scala.concurrent.duration.Duration | ||
import scala.concurrent._ | ||
import scala.util.Try | ||
|
||
/** | ||
* An object that waits for a DAGScheduler job to complete. As tasks finish, it passes their | ||
* results to the given handler function. | ||
|
@@ -26,19 +30,37 @@ private[spark] class JobWaiter[T]( | |
val jobId: Int, | ||
totalTasks: Int, | ||
resultHandler: (Int, T) => Unit) | ||
extends JobListener { | ||
extends JobListener with Future[Unit] { | ||
|
||
private[this] val promise: Promise[Unit] = { | ||
if (totalTasks == 0) { | ||
Promise.successful[Unit]() | ||
} else { | ||
Promise[Unit]() | ||
} | ||
} | ||
private[this] val promiseFuture: Future[Unit] = promise.future | ||
private[this] var finishedTasks = 0 | ||
|
||
override def onComplete[U](func: (Try[Unit]) => U)(implicit executor: ExecutionContext): Unit = { | ||
promiseFuture.onComplete(func) | ||
} | ||
|
||
private var finishedTasks = 0 | ||
override def isCompleted: Boolean = promiseFuture.isCompleted | ||
|
||
// Is the job as a whole finished (succeeded or failed)? | ||
@volatile | ||
private var _jobFinished = totalTasks == 0 | ||
override def value: Option[Try[Unit]] = promiseFuture.value | ||
|
||
def jobFinished: Boolean = _jobFinished | ||
@throws(classOf[Exception]) | ||
override def result(atMost: Duration)(implicit permit: CanAwait): Unit = { | ||
promiseFuture.result(atMost)(permit) | ||
} | ||
|
||
// If the job is finished, this will be its result. In the case of 0 task jobs (e.g. zero | ||
// partition RDDs), we set the jobResult directly to JobSucceeded. | ||
private var jobResult: JobResult = if (jobFinished) JobSucceeded else null | ||
@throws(classOf[InterruptedException]) | ||
@throws(classOf[TimeoutException]) | ||
override def ready(atMost: Duration)(implicit permit: CanAwait): this.type = { | ||
promiseFuture.ready(atMost)(permit) | ||
this | ||
} | ||
|
||
/** | ||
* Sends a signal to the DAGScheduler to cancel the job. The cancellation itself is handled | ||
|
@@ -50,28 +72,23 @@ private[spark] class JobWaiter[T]( | |
} | ||
|
||
override def taskSucceeded(index: Int, result: Any): Unit = synchronized { | ||
if (_jobFinished) { | ||
if (isCompleted) { | ||
throw new UnsupportedOperationException("taskSucceeded() called on a finished JobWaiter") | ||
} | ||
resultHandler(index, result.asInstanceOf[T]) | ||
finishedTasks += 1 | ||
if (finishedTasks == totalTasks) { | ||
_jobFinished = true | ||
jobResult = JobSucceeded | ||
this.notifyAll() | ||
promise.success() | ||
} | ||
} | ||
|
||
override def jobFailed(exception: Exception): Unit = synchronized { | ||
_jobFinished = true | ||
jobResult = JobFailed(exception) | ||
this.notifyAll() | ||
} | ||
|
||
def awaitResult(): JobResult = synchronized { | ||
while (!_jobFinished) { | ||
this.wait() | ||
// There are certain situations where jobFailed can be called multiple times for the same | ||
// job. We guard against this by making this method idempotent. | ||
if (!isCompleted) { | ||
promise.failure(exception) | ||
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. Looks |
||
} else { | ||
assert(promiseFuture.value.get.isFailure) | ||
} | ||
return jobResult | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what thread does this use? does it use some implicit thread pool?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's see... jobWaiter.result delegates to the job waiter's promise.future.result.
Here, I think we have an instance of DefaultPromise, whose
result
(https://github.com/scala/scala/blob/v2.10.4/src/library/scala/concurrent/impl/Promise.scala#L222) is implemented in terms ofready
(https://github.com/scala/scala/blob/v2.10.4/src/library/scala/concurrent/impl/Promise.scala#L218), which in turn is implemented using the internaltryAwait
method: https://github.com/scala/scala/blob/v2.10.4/src/library/scala/concurrent/impl/Promise.scala#L194It looks like this is implemented by scheduling an
onComplete
callback which updates a latch. This callback runs onFuture
's InternalCallbackExecutor: https://github.com/scala/scala/blob/v2.10.4/src/library/scala/concurrent/Future.scala#L590