Make progress an object

This commit reworks TaskProgress so that it is a singleton object. By
using a singleton, we ensure that there is at most one progress thread
running at a time. With multiple threads, there can be flickering in the
progress reports.

This fixes https://github.com/sbt/sbt/issues/5547. There also was a bug
that the reference to the progress thread was not reset when the thread
itself exited. As a result, it was possible for progress reporting to
stop while tasks were still running. This seemed to primarily happen in
multi-project builds. It should be fixed by this change.
This commit is contained in:
Ethan Atkins 2020-06-28 18:06:23 -07:00
parent a4e1c5aad5
commit db4878c786
2 changed files with 14 additions and 5 deletions

View File

@ -255,7 +255,7 @@ object EvaluateTask {
extracted,
structure
)
val reporters = maker.map(_.progress) ++ Some(new TaskProgress) ++
val reporters = maker.map(_.progress) ++ Some(TaskProgress) ++
(if (SysProp.taskTimings)
new TaskTimings(reportOnShutdown = false, state.globalLogging.full) :: Nil
else Nil)

View File

@ -16,14 +16,14 @@ import sbt.internal.util._
import scala.annotation.tailrec
import scala.concurrent.duration._
object TaskProgress extends TaskProgress
/**
* implements task progress display on the shell.
*/
private[sbt] final class TaskProgress
private[sbt] class TaskProgress private ()
extends AbstractTaskExecuteProgress
with ExecuteProgress[Task] {
@deprecated("Use the no argument constructor.", "1.4.0")
def this(log: ManagedLogger) = this()
private[this] val lastTaskCount = new AtomicInteger(0)
private[this] val currentProgressThread = new AtomicReference[Option[ProgressThread]](None)
private[this] val sleepDuration = SysProp.supershellSleep.millis
@ -38,6 +38,13 @@ private[sbt] final class TaskProgress
private[this] def doReport(): Unit = { hasReported.set(true); report() }
setDaemon(true)
start()
private def resetThread(): Unit =
currentProgressThread.synchronized {
currentProgressThread.getAndSet(None) match {
case Some(t) if t != this => currentProgressThread.set(Some(t))
case _ =>
}
}
@tailrec override def run(): Unit = {
if (!isClosed.get() && (!hasReported.get || active.nonEmpty)) {
try {
@ -61,6 +68,8 @@ private[sbt] final class TaskProgress
}
run()
} else {
resetThread()
}
}
@ -71,7 +80,7 @@ private[sbt] final class TaskProgress
interrupt()
report()
appendProgress(ProgressEvent("Info", Vector(), None, None, None))
()
resetThread()
}
}