diff --git a/node/src/main/kotlin/com/r3corda/node/utilities/ClockUtils.kt b/node/src/main/kotlin/com/r3corda/node/utilities/ClockUtils.kt index 09a2d69846..cc0d47d19d 100644 --- a/node/src/main/kotlin/com/r3corda/node/utilities/ClockUtils.kt +++ b/node/src/main/kotlin/com/r3corda/node/utilities/ClockUtils.kt @@ -37,7 +37,7 @@ abstract class MutableClock : Clock() { private val _version = AtomicLong(0L) /** - * This tracks how many direct mutations of "now" have occured for this [Clock], but not the passage of time. + * This tracks how many direct mutations of "now" have occurred for this [Clock], but not the passage of time. * * It starts at zero, and increments by one per mutation. */ @@ -79,22 +79,22 @@ abstract class MutableClock : Clock() { * * @throws InterruptedException if interrupted by something other than a [MutableClock]. */ -@Suppress("UNUSED_VALUE") // This is here due to the compiler thinking version is not used @Suspendable private fun Clock.doInterruptibly(runnable: SuspendableRunnable) { - var version = 0L var subscription: Subscription? = null + var interruptedByMutation = false try { if (this is MutableClock) { - version = this.mutationCount val strand = Strand.currentStrand() - subscription = this.mutations.subscribe { strand.interrupt() } + subscription = this.mutations.subscribe { + interruptedByMutation = true + strand.interrupt() + } } runnable.run() } catch(e: InterruptedException) { // If clock has not mutated, then re-throw - val newVersion = if (this is MutableClock) this.mutationCount else version - if (newVersion == version) { + if (!interruptedByMutation) { throw e } } finally { diff --git a/node/src/test/kotlin/com/r3corda/node/utilities/ClockUtilsTest.kt b/node/src/test/kotlin/com/r3corda/node/utilities/ClockUtilsTest.kt index be64fb16ca..508b061b12 100644 --- a/node/src/test/kotlin/com/r3corda/node/utilities/ClockUtilsTest.kt +++ b/node/src/test/kotlin/com/r3corda/node/utilities/ClockUtilsTest.kt @@ -16,6 +16,7 @@ import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import kotlin.test.assertFalse import kotlin.test.assertTrue +import kotlin.test.fail class ClockUtilsTest { @@ -112,6 +113,27 @@ class ClockUtilsTest { assertFalse(testClock.awaitWithDeadline(advancedClock.instant(), future), "Should have reached deadline") } + @Test + fun `test external interrupt of a clock future`() { + val mainStrand = Strand.currentStrand() + executor.execute @Suspendable { + // Wait until main thread is waiting + while (mainStrand.state != Strand.State.TIMED_WAITING) { + Strand.sleep(1) + } + mainStrand.interrupt() + } + + val testClock = TestClock(stoppedClock) + val advancedClock = Clock.offset(stoppedClock, Duration.ofHours(10)) + + try { + testClock.awaitWithDeadline(advancedClock.instant(), SettableFuture.create()) + fail("Expected InterruptedException") + } catch (exception: InterruptedException) { + } + } + /** * If this test seems to hang and throw an NPE, then likely that quasar suspendables scanner has not been * run on core module (in IntelliJ, open gradle side tab and run: