From b610ce9298d402791319c333b45de319eaba7a22 Mon Sep 17 00:00:00 2001 From: Ethan Atkins Date: Thu, 10 Sep 2020 13:35:53 -0700 Subject: [PATCH] Restore terminal when exiting Terminal.withStreams There was a reddit comment that the user's tty was messed up after they exited sbt: https://www.reddit.com/r/scala/comments/io3z2p/sbt_140rc1_released/. This attempts to fix that issue by restoring the terminal before exiting. In order to ensure the tty is restored, it's necessary to move the work off of a background thread and delay sbt exit. This does take about 150ms on my machine but I figure that isn't a huge deal in the scheme of things. --- .../scala/sbt/internal/util/Terminal.scala | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala index e5465fe63..347277c58 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala @@ -341,17 +341,21 @@ object Terminal { * to become non-blocking. After we set it to non-blocking, we spin * up a thread that reads from the inputstream and the resets it * back to blocking mode. We can then close the console. We do - * this on a background thread to avoid blocking sbt's exit. + * this on a background thread in case the read blocks indefinitely. */ val prev = c.system.enterRawMode() val runnable: Runnable = () => { - c.inputStream.read() - c.system.setAttributes(prev) - c.close() + try Util.ignoreResult(c.inputStream.read) + catch { case _: InterruptedException => } } val thread = new Thread(runnable, "sbt-console-background-close") thread.setDaemon(true) thread.start() + // The thread should exit almost instantly but give it 200ms to spin up + thread.join(200) + if (thread.isAlive) thread.interrupt() + c.system.setAttributes(prev) + c.close() case c => c.close() } } else { @@ -723,11 +727,10 @@ object Terminal { case _: InterruptedException | _: java.io.IOError => } override def restore(): Unit = - if (alive) - try terminal.restore() - catch { - case _: InterruptedException | _: java.io.IOError => - } + try terminal.restore() + catch { + case _: InterruptedException | _: java.io.IOError => + } override def reset(): Unit = try terminal.reset() catch { case _: InterruptedException => } @@ -864,8 +867,10 @@ object Terminal { case _ => false }) override def close(): Unit = { - try system.close() - catch { case NonFatal(_) => } + try { + system.close() + term.restore() + } catch { case NonFatal(_) => } super.close() } }