@@ -8,6 +8,9 @@ import java.io.{PrintWriter, StringWriter}
88import java .util .concurrent .CountDownLatch
99import scala .util .boundary .*
1010import scala .concurrent .duration .*
11+ import ox .OxApp .Settings
12+ import java .util .concurrent .atomic .AtomicLong
13+ import java .util .concurrent .{Semaphore , TimeUnit }
1114
1215class OxAppTest extends AnyFlatSpec with Matchers :
1316
@@ -25,6 +28,39 @@ class OxAppTest extends AnyFlatSpec with Matchers:
2528 ec shouldEqual 0
2629 }
2730
31+ " OxApp" should " shutdown despite cleanup taking a long time" in {
32+ var ec = Int .MinValue
33+
34+ val shutdownHookStarted = new AtomicLong (0L )
35+ val shutdownHookFinished = new AtomicLong (0L )
36+
37+ object Main15 extends OxApp :
38+ override protected def settings : Settings = Settings .Default .copy(shutdownTimeout = 100 .millis)
39+ override private [ox] def mountShutdownHook (thread : Thread ): Unit =
40+ Thread .sleep(100 ) // let the fork with the main logic start
41+ // running the shutdown hook - will interrupt the main logic
42+ shutdownHookStarted.set(System .currentTimeMillis())
43+ thread.start()
44+ thread.join()
45+ shutdownHookFinished.set(System .currentTimeMillis())
46+ end mountShutdownHook
47+
48+ override private [ox] def exit (exitCode : ExitCode ): Unit = ec = exitCode.code
49+
50+ override def run (args : Vector [String ])(using Ox ): ExitCode =
51+ try
52+ // will be interrupted by the shutdown hook
53+ never
54+ finally sleep(200 .millis) // simulating cleanup longer than the shutdown timeout
55+ end Main15
56+
57+ supervised :
58+ Main15 .main(Array .empty)
59+ ec shouldEqual 0
60+ (shutdownHookFinished.get() - shutdownHookStarted.get()) should be >= 100L
61+ (shutdownHookFinished.get() - shutdownHookStarted.get()) should be < 200L
62+ }
63+
2864 " OxApp" should " work in interrupted case" in {
2965 var ec = Int .MinValue
3066 val shutdownLatch = CountDownLatch (1 )
0 commit comments