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
Reimplement PipelineMain's sequencing to avoid races #8815
Conversation
This comment has been minimized.
This comment has been minimized.
89d8558
to
bd74039
Compare
I think the underlying problem is elsewhere. Maybe Reviewing the code, I think the root cause might be that diff --git a/src/compiler/scala/tools/nsc/PipelineMain.scala b/src/compiler/scala/tools/nsc/PipelineMain.scala
index ee97797447..e83a756032 100644
--- a/src/compiler/scala/tools/nsc/PipelineMain.scala
+++ b/src/compiler/scala/tools/nsc/PipelineMain.scala
@@ -154,9 +154,11 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe
val counter = new AtomicInteger(count)
val failed = new AtomicBoolean(false)
val handler = (a: Try[_]) => a match {
- case f @ Failure(_) =>
+ case f @ Failure(t) =>
if (failed.compareAndSet(false, true)) {
- done.complete(f)
+ val allFailures: immutable.Seq[Throwable] = allFutures.flatMap(_.value).flatMap(_.failed.toOption)
+ allFailures.foreach(_.printStackTrace())
+ done.complete(Failure(t))
}
case Success(_) =>
val remaining = counter.decrementAndGet()
@@ -431,6 +433,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe
reporter.flush()
else if (command.shouldStopWithInfo)
reporter.echo(command.getInfoMessage(result))
+ result.reporter = createReporter(result.settings)
result
} catch {
case t: Throwable =>
@@ -450,8 +453,8 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe
run1 compile files
outlineTimer.stop()
log(f"scalac outline: done ${outlineTimer.durationMs}%.0f ms")
- reporter.finish()
- if (reporter.hasErrors) {
+ compiler.reporter.finish()
+ if (compiler.reporter.hasErrors) {
log("scalac outline: failed")
outlineDone.complete(Failure(new RuntimeException(label + ": compile failed: ")))
} else {
@@ -460,8 +463,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe
}
} catch {
case t: Throwable =>
- t.printStackTrace()
- outlineDone.complete(Failure(new RuntimeException(label + ": compile failed: ")))
+ outlineDone.complete(Failure(new RuntimeException(label + ": compile failed: ", t)))
}
}
@@ -488,9 +490,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe
log(f"scalac (${ix + 1}/$groupCount): done ${group.timer.durationMs}%.0f ms")
}
if (compiler2.reporter.hasErrors) {
- group.done.complete(Failure(new RuntimeException(label + ": compile failed: ")))
- } else {
- group.done.complete(Success(()))
+ throw new RuntimeException(label + ": compile failed: ")
}
} finally {
compiler2.close()
@@ -575,7 +575,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe
log(f"javac: done ${javaTimer.durationMs}%.0f ms ")
} else {
javaTimer.stop()
- log(f"javac: error ${javaTimer.durationMs}%.0f ms ")
+ throw new RuntimeException(f"javac: error ${javaTimer.durationMs}%.0f ms ")
}
()
}) |
Why do you think it's elsewhere? It's a thrown NoSuchFileException after Files.exist check, which shouts "race condition" to me. But I'll throw your diff in too, then. |
scala/src/compiler/scala/tools/nsc/PipelineMain.scala Lines 152 to 170 in 2d9357e
In the above, |
You're missing a word there somewhere, so I don't understand what you're saying. Isn't the counter setting things up right? |
@dwijnand it's missing "before" -- the composed future would complete before the successful one is done. In case of a failure in one subproject, |
Right. What's wrong with that? |
When the test exits I am guessing that |
Maybe the correct building block we're after is: def sequenceFailSlow[A](fs: Seq[Future[A]]): Future[Seq[A]] = {
val futures: Seq[Future[Either[Throwable, A]]] = fs.map(_.transform(tr => Success(tr.toEither)))
Future.sequence(futures).map {
results =>
val successes = results.collect { case Right(value) => value }
val failures = results.collect { case Left(throwable) => throwable }.toList
failures match {
case head :: rest => rest.foreach(head.addSuppressed(_)); throw head
case Nil =>
successes
}
}
}
def awaitDone(): Unit = {
val allFutures: immutable.Seq[Future[_]] = projects.flatMap(_.futures)
val numAllFutures = allFutures.size
val awaitAllFutures: Future[_] = sequenceFailSlow[Any](allFutures) |
Echoing @eed3si9n's analysis: yes, this indicates a race. But if we remove the symptom of the |
And some other misc fixes & cleanups.
af430d4
to
eb8c64c
Compare
We've hit this a number of times:
-opt:help
&opt-warnings:help
#8683 (comment)And some other misc fixes & cleanups.