diff --git a/sbt-app/src/sbt-test/tests/fork-shutdown-hook/build.sbt b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/build.sbt new file mode 100644 index 000000000..1b63fb338 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/build.sbt @@ -0,0 +1,21 @@ +Global / localCacheDirectory := baseDirectory.value / "diskcache" +scalaVersion := "3.8.4" + +Test / fork := true + +libraryDependencies += "org.scalameta" %% "munit" % "1.0.4" % Test + +val check = TaskKey[Unit]("check", "Verify the shutdown hook could load classes.") + +check := Def.uncached { + val file = baseDirectory.value / "hook-result.txt" + val deadline = System.currentTimeMillis + 30000 + def content: String = if (file.exists) IO.read(file).trim else "" + while (content.isEmpty || content.startsWith("PENDING")) { + if (System.currentTimeMillis > deadline) + sys.error(s"shutdown hook never completed: '$content'") + Thread.sleep(500) + } + val result = content + if (!result.startsWith("OK")) sys.error(s"shutdown hook failed: '$result'") +} diff --git a/sbt-app/src/sbt-test/tests/fork-shutdown-hook/src/test/scala/LazilyLoaded.scala b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/src/test/scala/LazilyLoaded.scala new file mode 100644 index 000000000..1708e1fc1 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/src/test/scala/LazilyLoaded.scala @@ -0,0 +1,5 @@ +package repro + +object LazilyLoaded { + val marker: String = "loaded" +} diff --git a/sbt-app/src/sbt-test/tests/fork-shutdown-hook/src/test/scala/ShutdownHookTest.scala b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/src/test/scala/ShutdownHookTest.scala new file mode 100644 index 000000000..e741ab970 --- /dev/null +++ b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/src/test/scala/ShutdownHookTest.scala @@ -0,0 +1,20 @@ +package repro + +import java.nio.file.{ Files, Paths } + +class ShutdownHookTest extends munit.FunSuite { + test("shutdown hook can load classes after the test run") { + val out = Paths.get("hook-result.txt") + Files.writeString(out, "PENDING: shutdown hook did not run" + System.lineSeparator) + val _ = sys.addShutdownHook { + val result = + try { + Class.forName("repro.LazilyLoaded$") + "OK: classloader still works at JVM shutdown" + } catch { + case t: Throwable => s"FAIL: $t" + } + Files.writeString(out, result + System.lineSeparator) + } + } +} diff --git a/sbt-app/src/sbt-test/tests/fork-shutdown-hook/test b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/test new file mode 100644 index 000000000..21544ae1c --- /dev/null +++ b/sbt-app/src/sbt-test/tests/fork-shutdown-hook/test @@ -0,0 +1,2 @@ +> testFull +> check diff --git a/worker/src/main/java/sbt/internal/worker1/WorkerMain.java b/worker/src/main/java/sbt/internal/worker1/WorkerMain.java index 7632a516c..7cd0617f6 100644 --- a/worker/src/main/java/sbt/internal/worker1/WorkerMain.java +++ b/worker/src/main/java/sbt/internal/worker1/WorkerMain.java @@ -188,12 +188,11 @@ public final class WorkerMain { throw new RuntimeException("missing jvmRunInfo element"); } RunInfo.JvmRunInfo jvmRunInfo = info.jvmRunInfo; - try (URLClassLoader cl = createClassLoader(jvmRunInfo, ClassLoader.getSystemClassLoader())) { - Class mainClass = cl.loadClass(jvmRunInfo.mainClass); - Method mainMethod = mainClass.getMethod("main", String[].class); - String[] mainArgs = jvmRunInfo.args.stream().toArray(String[]::new); - mainMethod.invoke(null, (Object) mainArgs); - } + URLClassLoader cl = createClassLoader(jvmRunInfo, ClassLoader.getSystemClassLoader()); + Class mainClass = cl.loadClass(jvmRunInfo.mainClass); + Method mainMethod = mainClass.getMethod("main", String[].class); + String[] mainArgs = jvmRunInfo.args.stream().toArray(String[]::new); + mainMethod.invoke(null, (Object) mainArgs); } else { throw new RuntimeException("only jvm is supported"); } @@ -207,9 +206,8 @@ public final class WorkerMain { if (jvmRunInfo.classpath.isEmpty()) { ForkTestMain.main(id, info, this.jsonOut, parent); } else { - try (URLClassLoader cl = createClassLoader(jvmRunInfo, parent)) { - ForkTestMain.main(id, info, this.jsonOut, cl); - } + URLClassLoader cl = createClassLoader(jvmRunInfo, parent); + ForkTestMain.main(id, info, this.jsonOut, cl); } } else { throw new RuntimeException("only jvm is supported");