From 7a7f34f4adb11d9c16075144a580f8e82c2bb086 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 12 Jun 2024 13:01:58 +0200 Subject: [PATCH] Defensive access to volatile ScheduledFuture field Includes defensive test arrangement for isInThePast() with at least 1 ms having passed. See gh-24560 --- .../scheduling/config/ScheduledTask.java | 5 +++-- .../scheduling/config/TaskTests.java | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTask.java b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTask.java index 9b6e7f7c0172..4f0265237e31 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTask.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/ScheduledTask.java @@ -86,8 +86,9 @@ public void cancel(boolean mayInterruptIfRunning) { */ @Nullable public Instant nextExecution() { - if (this.future != null && !this.future.isCancelled()) { - long delay = this.future.getDelay(TimeUnit.MILLISECONDS); + ScheduledFuture future = this.future; + if (future != null && !future.isCancelled()) { + long delay = future.getDelay(TimeUnit.MILLISECONDS); if (delay > 0) { return Instant.now().plusMillis(delay); } diff --git a/spring-context/src/test/java/org/springframework/scheduling/config/TaskTests.java b/spring-context/src/test/java/org/springframework/scheduling/config/TaskTests.java index 685024f71cb9..a122978625ac 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/config/TaskTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/config/TaskTests.java @@ -24,7 +24,9 @@ /** * Tests for {@link Task}. + * * @author Brian Clozel + * @since 6.2 */ class TaskTests { @@ -77,20 +79,32 @@ static class TestRunnable implements Runnable { @Override public void run() { + try { + Thread.sleep(1); + } + catch (InterruptedException ex) { + throw new RuntimeException(ex); + } this.hasRun = true; } } + static class FailingTestRunnable implements Runnable { boolean hasRun; @Override public void run() { + try { + Thread.sleep(1); + } + catch (InterruptedException ex) { + throw new RuntimeException(ex); + } this.hasRun = true; throw new IllegalStateException("test exception"); } } - }