JDK-8051859 : ScheduledExecutorService.scheduleWithFixedDelay fails with max delay
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 7,8u11,9
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86
  • Submitted: 2012-09-18
  • Updated: 2015-11-09
  • Resolved: 2015-10-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b88Resolved
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java -version
java version "1.7.0_07"
Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
Java HotSpot(TM) Client VM (build 23.3-b01, mixed mode, sharing)

A DESCRIPTION OF THE PROBLEM :
When ScheduledExecutorService.scheduleWithFixedDelay is called with a delay of Long.MAX_VALUE and a TimeUnit longer than NANOSECONDS it fails badly, putting the executor service into an unusable state.
The problem is due to the internal storage of the delay as a negative number in the "period" field of the ScheduledFutureTask (positive numbers mean fixed-rate rather than fixed-delay).  The period field is set to TimeUnit.toNanos(-delay), and if the delay is large enough and the TimeUnit is longer than NANOSECONDS then this will be equal to Long.MIN_VALUE.  Later, when the period field is used in .ScheduledThreadPoolExecutor.ScheduledFutureTask.setNextRunTime() it is converted back to a positive number with the unary minus operator before being passed to the triggerTime method.  However, -Long.MIN_VALUE is a negative number (since the magnitude of Long.MIN_VALUE is one greater than the magnitude of Long.MAX_VALUE)!  This results in triggerTime returning a time in the distant past.




REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledTaskBug
{
    static public void main(String[] args) throws InterruptedException
    {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleWithFixedDelay(new Runnable()
        {

            @Override
            public void run()
            {
                System.out.println("running scheduled task with delay: " + new Date());
            }
        }, 0, Long.MAX_VALUE, TimeUnit.MICROSECONDS);
        // This second task should run right after the first run of the previously task scheduled
        // with a delay.
        // However, due to a bug in scheduleWithFixedDelay, it never runs. If the TimeUnit above is
        // changed to TimeUnit.NANOSECONDS then the following task will run as expected.
        executor.submit(new Runnable()
        {

            @Override
            public void run()
            {
                System.out.println("running immediate task: " + new Date());
            }
        });
        Thread.sleep(5000);
        executor.shutdownNow();

    }
}

---------- END SOURCE ----------

Comments
Yet another bug to be fixed as part of the Great jsr166 jdk9 integration.
12-10-2015

Here's the horrifying bug fix: If the timeunit is not NANOSECONDS, and the period causes overflow, then period ends up being Long.MIN_VALUE instead of -Long.MAX_VALUE as probably intended, and when Long.MIN_VALUE is negated, it stays negative! diff -u -r1.96 ScheduledThreadPoolExecutor.java --- src/main/java/util/concurrent/ScheduledThreadPoolExecutor.java 3 Oct 2015 18:17:51 -0000 1.96 +++ src/main/java/util/concurrent/ScheduledThreadPoolExecutor.java 12 Oct 2015 16:30:40 -0000 @@ -596,7 +596,7 @@ new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), - unit.toNanos(-delay), + -unit.toNanos(delay), sequencer.getAndIncrement()); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t;
12-10-2015

Here's a tck test that demonstrates the problem: --- src/test/tck/ScheduledExecutorTest.java 8 Oct 2015 03:08:37 -0000 1.76 +++ src/test/tck/ScheduledExecutorTest.java 12 Oct 2015 16:30:40 -0000 @@ -1206,4 +1206,27 @@ } } + /** + * A fixed delay task with overflowing period should not prevent a + * one-shot task from executing. + * https://bugs.openjdk.java.net/browse/JDK-8051859 + */ + public void testScheduleWithFixedDelay_overflow() throws Exception { + final CountDownLatch delayedDone = new CountDownLatch(1); + final CountDownLatch immediateDone = new CountDownLatch(1); + final ScheduledThreadPoolExecutor p = new ScheduledThreadPoolExecutor(1); + try (PoolCleaner cleaner = cleaner(p)) { + final Runnable immediate = new Runnable() { public void run() { + immediateDone.countDown(); + }}; + final Runnable delayed = new Runnable() { public void run() { + delayedDone.countDown(); + p.submit(immediate); + }}; + p.scheduleWithFixedDelay(delayed, 0L, Long.MAX_VALUE, SECONDS); + await(delayedDone); + await(immediateDone); + } + } + }
12-10-2015

This is confirmed to be still a problem in upstream jsr166 CVS.
12-10-2015