I asked this question on discord and got no response. I’ll reäsk it here as perhaps the answer is not so trivial.
Does anyone know something about org.scalatest.concurrent.TimeLimits.failAfter ? I have the following test that I waiting about 45 minutes to an hour for it to complete. I finally killed it.
test("test_extract_rte") {
// test should fail if cannot run within 60 seconds
// perhaps need to adjust number of seconds
failAfter(Span(60000, Millis)) {
SAtomic.withClosedWorldView {
for {depth <- 1 to 3
rep <- 0 to 25
rt = Rte.randomRte(depth)
} check_extraction_cycle(rt, depth, rep)
}
}
}
Does failAfter somehow work differently than I thought it would?
It’s “always fail on excessive duration, but don’t fail fast once duration becomes excessive”. Which may not be immediately intuitive, but it makes sense - as the different implementations suggest, there is no single “right” way of halting a computation, and interrupting a thread potentially has harmful side effects.
Like @sangamon said, there is no one right way to “cancel” a random piece of code. It may even not be cancelable at all, especially if it’s only doing heavy computations on the CPU or infinitely looping without checking for thread interrupts.
I really don’t understand how it’s supposed to work. I’m trying to implement a timeout of 1 minute. I’ve added the line as follows, left for lunch at 12h15, came back at 13h30, and it’s still running.
test("test_extract_rte") {
// test should fail if cannot run within 60 seconds
// perhaps need to adjust number of seconds
implicit val signaler: Signaler = ThreadSignaler
failAfter(Span(60000, Millis)) {
SAtomic.withClosedWorldView {
for {depth <- 1 to 3
rep <- 0 to 25
rt = Rte.randomRte(depth)
} check_extraction_cycle(rt, depth, rep)
}
}
}
Until you have an uncancelable IO But indeed having a sane way to compose a program with built-in and well-defined cancelation semantics is a big improvement.
I use ScalaTest to grade student code. Timeouts are a big issue, as their code typically doesn’t react to interrupts. I use various tricks, but there’s no perfect solution. I do use this little construct everywhere I can (i.e., when loops are in my tests, not their code):
@throws[InterruptedException]
inline def interruptibly[A](inline code: A): A =
if Thread.interrupted() then throw InterruptedException() else code