Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions integration/invalidation/run-background/resources/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package build
import mill._, javalib._

object foo extends JavaModule{
def runBackgroundLogToConsole: Boolean = false
}
Original file line number Diff line number Diff line change
@@ -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!");
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
}
3 changes: 2 additions & 1 deletion main/util/src/mill/util/Jvm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}

Expand Down
Loading