DelayScheduler.replace method may break the 4-ary heap in certain scenarios

XMLWordPrintable

        A DESCRIPTION OF THE PROBLEM :
        When performing sift-down operation, the moved "t' may not be a child of the target place, causing the min-heap structure being broken.

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Execute the test code.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        Tasks are executed in the ascending order of their respective delay, outputting the following content:

        Delay 200
        Delay 310
        Delay 420
        Delay 430
        Delay 400
        Delay 500
        Delay 700
        Delay 800
        Delay 900
        ACTUAL -
        The output is as following. Note the 400-delayed task is executed after the 420 and 430 ones.

        Delay 200
        Delay 310
        Delay 400
        Delay 420
        Delay 430
        Delay 500
        Delay 700
        Delay 800
        Delay 900

        ---------- BEGIN SOURCE ----------
        import java.util.concurrent.CountDownLatch;
        import java.util.concurrent.ForkJoinPool;
        import java.util.concurrent.ScheduledFuture;
        import java.util.concurrent.TimeUnit;

        public class Application {

            private static final long[] DELAYS = new long[] {400, 900, 800, 700, 600, 430, 420, 310, 500, 200};
            private static final int DELAY_OFFSET = 5000;
            private static final CountDownLatch LATCH = new CountDownLatch(DELAYS.length);

            public static void main(String[] args) throws Exception {

                ScheduledFuture<?>[] tasks = new ScheduledFuture[DELAYS.length];
                ForkJoinPool pool = ForkJoinPool.commonPool();
                for (int i = 0; i < DELAYS.length; ++i) {
                    TestRunnable runnable = new TestRunnable(DELAYS[i]);
                    tasks[i] = pool.schedule(runnable, runnable.delay + DELAY_OFFSET, TimeUnit.MILLISECONDS);
                }
                Thread.sleep(50);
                tasks[4].cancel(true);
                LATCH.countDown();
                LATCH.await();
            }

            private static class TestRunnable implements Runnable {

                private final long delay;

                public TestRunnable(long delay) {
                    this.delay = delay;
                }

                @Override
                public void run() {
                    synchronized (TestRunnable.class) {
                        System.out.println("Delay " + delay);
                    }
                    LATCH.countDown();
                }

                @Override
                public String toString() {
                    return "toString called for delay " + delay;
                }
            }
        }

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

              Assignee:
              Alan Bateman
              Reporter:
              Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              11 Start watching this issue

                Created:
                Updated:
                Resolved: