From f0bec6d9e3163c7417dd1d703cd84eb9c012346d Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 24 Sep 2019 10:30:25 -0700 Subject: [PATCH 1/2] Limit TaskProgress threads I noticed some flickering in super shell progress lines and realized that it was because there were multiple progress threads running concurrently. This is problematic because each thread has a completely different state so if each thread has an active task, the display will flicker between the two tasks. I think this is caused primarily by dynamic tasks. At least the example where I was seeing it was caused by a dynamic task. --- main/src/main/scala/sbt/EvaluateTask.scala | 48 ++++++++++++---------- main/src/main/scala/sbt/Keys.scala | 1 + main/src/main/scala/sbt/MainLoop.scala | 15 +++++-- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index a5e313464..b23ab9105 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -224,29 +224,33 @@ object EvaluateTask { structure: BuildStructure, state: State ): ExecuteProgress[Task] = { - val maker: Seq[Keys.TaskProgress] = getSetting( - Keys.progressReports, - Seq(), - extracted, - structure - ) - val progressReporter = extracted.get(progressState in ThisBuild).map { ps => - ps.reset() - ConsoleAppender.setShowProgress(true) - val appender = MainAppender.defaultScreen(StandardMain.console) - appender match { - case c: ConsoleAppender => c.setProgressState(ps) - case _ => + state.get(currentTaskProgress).map(_.progress).getOrElse { + val maker: Seq[Keys.TaskProgress] = getSetting( + Keys.progressReports, + Seq(), + extracted, + structure + ) + val progressReporter = extracted.getOpt(progressState in ThisBuild).flatMap { + case Some(ps) => + ps.reset() + ConsoleAppender.setShowProgress(true) + val appender = MainAppender.defaultScreen(StandardMain.console) + appender match { + case c: ConsoleAppender => c.setProgressState(ps) + case _ => + } + val log = LogManager.progressLogger(appender) + Some(new TaskProgress(log)) + case _ => None + } + val reporters = maker.map(_.progress) ++ progressReporter ++ + (if (SysProp.taskTimings) new TaskTimings(reportOnShutdown = false) :: Nil else Nil) + reporters match { + case xs if xs.isEmpty => ExecuteProgress.empty[Task] + case xs if xs.size == 1 => xs.head + case xs => ExecuteProgress.aggregate[Task](xs) } - val log = LogManager.progressLogger(appender) - new TaskProgress(log) - } - val reporters = maker.map(_.progress) ++ progressReporter ++ - (if (SysProp.taskTimings) new TaskTimings(reportOnShutdown = false) :: Nil else Nil) - reporters match { - case xs if xs.isEmpty => ExecuteProgress.empty[Task] - case xs if xs.size == 1 => xs.head - case xs => ExecuteProgress.aggregate[Task](xs) } } // TODO - Should this pull from Global or from the project itself? diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 595b77f98..ae957c369 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -482,6 +482,7 @@ object Keys { object TaskProgress { def apply(progress: ExecuteProgress[Task]): TaskProgress = new TaskProgress(progress) } + private[sbt] val currentTaskProgress = AttributeKey[TaskProgress]("current-task-progress") val useSuperShell = settingKey[Boolean]("Enables (true) or disables the super shell.") val turbo = settingKey[Boolean]("Enables (true) or disables optional performance features.") // This key can be used to add custom ExecuteProgress instances diff --git a/main/src/main/scala/sbt/MainLoop.scala b/main/src/main/scala/sbt/MainLoop.scala index 5ee1b738e..c4351a210 100644 --- a/main/src/main/scala/sbt/MainLoop.scala +++ b/main/src/main/scala/sbt/MainLoop.scala @@ -179,10 +179,18 @@ object MainLoop { val channelName = exec.source map (_.channelName) StandardMain.exchange publishEventMessage ExecStatusEvent("Processing", channelName, exec.execId, Vector()) - try { def process(): State = { - val newState = Command.process(exec.commandLine, state) + val progressState = state.get(sbt.Keys.currentTaskProgress) match { + case Some(_) => state + case _ => + if (state.get(Keys.stateBuildStructure).isDefined) { + val extracted = Project.extract(state) + val progress = EvaluateTask.executeProgress(extracted, extracted.structure, state) + state.put(sbt.Keys.currentTaskProgress, new Keys.TaskProgress(progress)) + } else state + } + val newState = Command.process(exec.commandLine, progressState) if (exec.commandLine.contains("session")) newState.get(hasCheckedMetaBuild).foreach(_.set(false)) val doneEvent = ExecStatusEvent( @@ -198,7 +206,8 @@ object MainLoop { } else { // send back a notification StandardMain.exchange publishEventMessage doneEvent } - newState + newState.get(sbt.Keys.currentTaskProgress).foreach(_.progress.stop()) + newState.remove(sbt.Keys.currentTaskProgress) } // The split on space is to handle 'reboot full' and 'reboot'. state.currentCommand.flatMap(_.commandLine.trim.split(" ").headOption) match { From edd21b0ec8e707f61f0dd3e22fd790ac94ea7fa5 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Tue, 24 Sep 2019 10:30:32 -0700 Subject: [PATCH 2/2] Filter out dummy tasks from progress I don't think that dummy tasks really make sense for task progress because they are evaluated outside of the normal task evaluation. This came up because I was seeing streams-manager in supershell which didn't seem useful. --- main/src/main/scala/sbt/internal/TaskProgress.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/internal/TaskProgress.scala b/main/src/main/scala/sbt/internal/TaskProgress.scala index f792b3b14..35ceaeb0c 100644 --- a/main/src/main/scala/sbt/internal/TaskProgress.scala +++ b/main/src/main/scala/sbt/internal/TaskProgress.scala @@ -73,7 +73,7 @@ private[sbt] final class TaskProgress(log: ManagedLogger) private[this] val skipReportTasks = Set("run", "bgRun", "fgRun", "scala", "console", "consoleProject", "consoleQuick", "state") private[this] def report(): Unit = { - val currentTasks = activeTasks.toVector + val currentTasks = activeTasks.toVector.filterNot(Def.isDummy) val ltc = lastTaskCount.get val currentTasksCount = currentTasks.size def report0(): Unit = {