From 3c9826f4f4f9a55983b61dc31b23bf9e45d81d64 Mon Sep 17 00:00:00 2001 From: Amina Adewusi Date: Fri, 20 Aug 2021 17:45:14 +0100 Subject: [PATCH] Fixes shutdown hook error in timing report This fixes the closed channel exception generated when running sbt -timings help. This bug was introduced in sbt 1.4 where a wrapper was created (Terminal.scala) around the terminal. This meant that the shutdown hook which had been used previously was no longer working. This has been fixed by avoiding the use of the JVM shutdown hook and instead manually running the thunk at a place in the code where the programme is still able to respond. --- main/src/main/scala/sbt/EvaluateTask.scala | 24 +++++++++++++++++++ .../scala/sbt/internal/CommandExchange.scala | 1 + .../main/scala/sbt/internal/TaskTimings.scala | 3 ++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index 55da21c94..8d45104cb 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -9,6 +9,7 @@ package sbt import java.io.File import java.util.concurrent.atomic.AtomicReference + import sbt.Def.{ ScopedKey, Setting, dummyState } import sbt.Keys.{ TaskProgress => _, name => _, _ } import sbt.Project.richInitializeTask @@ -28,6 +29,7 @@ import sbt.internal.bsp.BuildTargetIdentifier import scala.annotation.nowarn import scala.Console.RED import scala.concurrent.duration.Duration +import scala.util.control.NonFatal /** * An API that allows you to cancel executing tasks upon some signal. @@ -169,6 +171,28 @@ object EvaluateTask { if (SysProp.taskTimingsOnShutdown) Some(sharedProgress) else None + private val capturedThunk = new AtomicReference[() => Unit]() + def onShutdown(): Unit = { + val thunk = capturedThunk.getAndSet(null) + if (thunk != null) thunk() + } + // our own implementation of shutdown hook, because the "sbt -timings help" command was not working with the JVM shutdown hook, + // which is a little hard to control. + def addShutdownHandler[A](thunk: () => A): Unit = { + capturedThunk + .set( + () => + try { + thunk() + () + } catch { + case NonFatal(e) => + System.err.println(s"Caught exception running shutdown hook: $e") + e.printStackTrace(System.err) + } + ) + } + lazy private val sharedTraceEvent = new TaskTraceEvent() def taskTraceEvent: Option[ExecuteProgress[Task]] = if (SysProp.traces) { diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 36059cdc3..992fb8548 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -290,6 +290,7 @@ private[sbt] final class CommandExchange { // interrupt and kill the thread server.foreach(_.shutdown()) server = None + EvaluateTask.onShutdown } // This is an interface to directly respond events. diff --git a/main/src/main/scala/sbt/internal/TaskTimings.scala b/main/src/main/scala/sbt/internal/TaskTimings.scala index 6f421e829..fe814ccd5 100644 --- a/main/src/main/scala/sbt/internal/TaskTimings.scala +++ b/main/src/main/scala/sbt/internal/TaskTimings.scala @@ -8,6 +8,7 @@ package sbt package internal +import sbt.EvaluateTask.addShutdownHandler import sbt.internal.util.{ ConsoleOut, RMap } import sbt.util.{ Level, Logger } @@ -38,7 +39,7 @@ private[sbt] final class TaskTimings(reportOnShutdown: Boolean, logger: Logger) if (reportOnShutdown) { start = System.nanoTime - ShutdownHooks.add(() => report()) + addShutdownHandler(() => report()) } override def initial(): Unit = {