Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 6639183
Votes 0
Synopsis Scheduling large negative delay hangs entire ScheduledExecutor
Category java:classes_util_concurrent
Reported Against
Release Fixed 6u10(b10)
State 10-Fix Delivered, bug
Priority: 2-High
Related Bugs 6725789
Submit Date 07-DEC-2007
Description
type: bug
product: j2se
sub-cat: java.util.*
release: Java 6
O/S: Windows XP

Synopsis: Scheduling large negative delay hangs entire ScheduledExecutor

Full O/S version:  customer  Windows XP [Version 5.1.2600]
java -version: 

both

java version "1.5.0_12"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_12-b04)
Java HotSpot(TM) Client VM (build 1.5.0_12-b04, mixed mode)

and

java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Client VM (build 1.6.0_03-b05, mixed mode)


Description:

Scheduling something with a very large negative delay (using TimeUnit.MILLISECONDS) blocks the scheduled executor from running anything.  Other than perhaps relative ordering, negative delay (no matter how large) is effectively the same as zero delay and the scheduled item ought to be eligible to run immediately.

Frequency: always

Regression: no

Steps to reproduce:

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ScheduleBug {
    public static void main(String[] args) {
        ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);

        exec.schedule(new Op("one"), -1000, TimeUnit.MILLISECONDS);
        exec.schedule(new Op("two"), -100, TimeUnit.MILLISECONDS);
        exec.schedule(new Op("three"), -10, TimeUnit.MILLISECONDS);
        exec.schedule(new Op("four"), 0, TimeUnit.MILLISECONDS);
        exec.schedule(new Op("five"), 1000, TimeUnit.MILLISECONDS);
        exec.schedule(new Op("six"), 5000, TimeUnit.MILLISECONDS);
        exec.schedule(new Runnable() {
            public void run() { System.exit(0); }
        }, 10000, TimeUnit.MILLISECONDS);
    }


    static class Op implements Runnable {
        private final String m_message;

        public Op(String message)
        {   m_message = message;    }

        public void run() {
            System.out.println(m_message);
            System.out.flush();
        }
    }
}

produces the following output as expected (after 10-15s):
one
two
three
four
five
six


However, change the first schedule line from
        exec.schedule(new Op("one"), -1000, TimeUnit.MILLISECONDS);
to
        exec.schedule(new Op("one"), Long.MIN_VALUE, TimeUnit.MILLISECONDS);

and the program will hang for hours with no output at all.

problems:

1) should be exact same output.
2) Even if underflow were documented / specified behavior, scheduling op "one" far in the future should NOT prevent other ops from running right away

severity: some  customer  possible
Posted Date : 2007-12-07 21:06:33.0
Work Around
manually change negative values to zero before scheduling
Evaluation
This is reproducible with jdk 7-b07 and 6u5,
but no longer reproducible in jdk 7-b08 
which had a large complex rewrite of much of
ThreadPoolExecutor and ScheduledThreadPoolExecutor

Here's a smaller test case demonstrating the problem:

import java.util.concurrent.ScheduledThreadPoolExecutor;
import static java.util.concurrent.TimeUnit.*;

public class Bug {
    public static void main(String[] args) throws Throwable {
        ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(1);

        pool.schedule(new Runnable() { public void run() {
            System.out.println("Age of the dinosaurs"); }},
                      Long.MIN_VALUE, NANOSECONDS);
        pool.schedule(new Runnable() { public void run() {
            System.out.println("Yesterday, all my troubles ..."); }},
                      -1L, DAYS);
        pool.shutdown();
        pool.awaitTermination(10L, SECONDS);
    }
}

Here's the list of CRs fixed in jdk 7-b08

6450200: ThreadPoolExecutor idling core threads don't terminate when core pool size reduced
6450205: ThreadPoolExecutor does not replace throwing threads
6450207: ThreadPoolExecutor doesn't count throwing tasks as "completed"
6450211: ThreadPoolExecutor.afterExecute sees RuntimeExceptions, but not Errors
6454289: ScheduledThreadPoolExecutor spins while waiting for delayed tasks after shutdown
6458339: ThreadPoolExecutor very slow to shut down for large poolSize
6458662: ThreadPoolExecutor poolSize might shrink below corePoolSize after timeout
6459119: Explain how afterExecute can access a submitted job's Throwable

I don't know what exactly changed to cause this bug to go away.
Posted Date : 2007-12-07 21:57:20.0

Looking closer....
Changing the Runnable to a Callable

        pool.schedule(
            new Callable<Void>() { public Void call() {
                    System.out.println("Age of the dinosaurs");
                    return null; }},
            Long.MIN_VALUE, NANOSECONDS);

makes the bug go away.
Which strongly hints that this easy change make in 7-b08
should be backported:

--- /tmp/geta27573	2007-12-07 14:31:24.283954000 -0800
+++ ScheduledThreadPoolExecutor.java	2007-12-07 14:30:45.394875000 -0800
@@ -355,14 +355,15 @@
     }
 
     public ScheduledFuture<?> schedule(Runnable command,
                                        long delay,
                                        TimeUnit unit) {
         if (command == null || unit == null)
             throw new NullPointerException();
+        if (delay < 0) delay = 0;
         long triggerTime = now() + unit.toNanos(delay);
         RunnableScheduledFuture<?> t = decorateTask(command,
             new ScheduledFutureTask<Boolean>(command, null, triggerTime));
         delayedExecute(t);
         return t;
     }
 
It appears that this problem is due to large negative values underflowing.
This problem may appear elsewhere in java.util.concurrent.
Posted Date : 2007-12-07 22:34:42.0

David Holmes writes,

"The reason I think it hangs (it's a tricky one to puzzle out) is because the bad Delayed item gets correctly queued at the head of the delay queue, but because of the way the initial delay is stored and the way getDelayed() works, you get an underflow that turns the delay into a large positive value - hence no Delayed entries get processed because this is the one with the next delay and it claims not to be ready."

which makes sense to me.
Posted Date : 2007-12-09 02:37:27.0

Note that the fix for 6725789 subsumes/replaces the simple fix suggested here.
Posted Date : 2008-08-20 10:46:47.0
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang