diff --git a/integration/invalidation/run-background/resources/build.mill b/integration/invalidation/run-background/resources/build.mill new file mode 100644 index 000000000000..bcda4c331060 --- /dev/null +++ b/integration/invalidation/run-background/resources/build.mill @@ -0,0 +1,6 @@ +package build +import mill._, javalib._ + +object foo extends JavaModule{ + def runBackgroundLogToConsole: Boolean = false +} \ No newline at end of file diff --git a/integration/invalidation/run-background/resources/foo/src/foo/Foo.java b/integration/invalidation/run-background/resources/foo/src/foo/Foo.java new file mode 100644 index 000000000000..4b6e49b0d35b --- /dev/null +++ b/integration/invalidation/run-background/resources/foo/src/foo/Foo.java @@ -0,0 +1,20 @@ +package foo; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.file.*; +public class Foo { + public static void main(String[] args) throws Exception{ + System.out.println("Hello World A!"); + RandomAccessFile raf = new RandomAccessFile(args[0], "rw"); + System.out.println("Hello World B!"); + FileChannel chan = raf.getChannel(); + System.out.println("Hello World C!"); + chan.lock(); + System.out.println("Hello World D!"); + while(true){ + if (!Files.exists(Paths.get(args[1]))) Thread.sleep(1); + else break; + } + System.out.println("Hello World E!"); + } +} diff --git a/integration/invalidation/run-background/src/RunBackgroundTests.scala b/integration/invalidation/run-background/src/RunBackgroundTests.scala new file mode 100644 index 000000000000..090e31585927 --- /dev/null +++ b/integration/invalidation/run-background/src/RunBackgroundTests.scala @@ -0,0 +1,58 @@ +package mill.integration + +import mill.testkit.UtestIntegrationTestSuite +import java.io.RandomAccessFile +import utest.asserts.{RetryInterval, RetryMax} +import scala.concurrent.duration._ +import utest._ + +// Make sure that `runBackground` subprocesses properly outlive the execution +// that created them, and outlive the Mill process whether terminated naturally +// at the end of `--no-server` or terminated explicitly via `shutdown` +object RunBackgroundTests extends UtestIntegrationTestSuite { + implicit val retryMax: RetryMax = RetryMax(5000.millis) + implicit val retryInterval: RetryInterval = RetryInterval(50.millis) + + def probeLockAvailable(lock: os.Path): Boolean = { + val raf = new RandomAccessFile(lock.toIO, "rw"); + val chan = raf.getChannel(); + chan.tryLock() match { + case null => false + case locked => + locked.release() + true + } + } + + val tests: Tests = Tests { + test("simple") - integrationTest { tester => + import tester._ + val lock = os.temp() + val stop = os.temp() + os.remove(stop) + eval(("foo.runBackground", lock, stop)) + eventually { !probeLockAvailable(lock) } + if (tester.clientServerMode) eval("shutdown") + continually { !probeLockAvailable(lock) } + os.write(stop, "") + eventually { probeLockAvailable(lock) } + } + test("clean") - integrationTest { tester => + if (!mill.main.client.Util.isWindows) { + import tester._ + val lock = os.temp() + val stop = os.temp() + os.remove(stop) + eval(("foo.runBackground", lock, stop)) + eventually { + !probeLockAvailable(lock) + } + + eval(("clean", "foo.runBackground")) + eventually { + probeLockAvailable(lock) + } + } + } + } +} diff --git a/main/util/src/mill/util/Jvm.scala b/main/util/src/mill/util/Jvm.scala index df656a7cebd7..64098054d8b1 100644 --- a/main/util/src/mill/util/Jvm.scala +++ b/main/util/src/mill/util/Jvm.scala @@ -395,7 +395,8 @@ object Jvm extends CoursierSupport { env = envArgs, stdin = if (backgroundOutputs.isEmpty) os.Inherit else "", stdout = backgroundOutputs.map(_._1).getOrElse(os.Inherit), - stderr = backgroundOutputs.map(_._2).getOrElse(os.Inherit) + stderr = backgroundOutputs.map(_._2).getOrElse(os.Inherit), + destroyOnExit = backgroundOutputs.isEmpty ) }