JDK-7146994 : example afterExecute for ScheduledThreadPoolExecutor hangs
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86
  • Submitted: 2012-02-19
  • Updated: 2015-11-09
  • Resolved: 2015-09-26
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 :
JDK 1.7.0_02

ADDITIONAL OS VERSION INFORMATION :
Windows 7 x64

A DESCRIPTION OF THE PROBLEM :
When using a subclassed ScheduledThreadPoolExecutor with the example  afterExecute method provided in the documentation, the implementation will hang on the
Object result = ((Future<?>) r).get();
line. The reason is as follows:
for a scheduledThreadPool afterExecute is called after every timed execution ( this is possible another bug as various docs on the internet imply it is only called after the task is cancelled or fully completed, but that is another issue) however, the call to get() tries to get a shared lock which only succeeds once the task has completed. Therefore afterExecute hangs on this lock indefinitely as the task is rescheduled for another execution.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The provided code fails each time it is run



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The program should continue to print "something" every 1 second for ever (or until stopped)
ACTUAL -
something is printed 5 times (once for each thread in the pool) before hanging indefinitely.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

package tasktests;

import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TaskTests {

    private static class MyExecutor extends ScheduledThreadPoolExecutor {
        public MyExecutor(){
            super(5);
        }
        protected void afterExecute(Runnable r, Throwable t){
            super.afterExecute(r, t);
            if (t == null && r instanceof Future<?> && ((Future<?>)r).isDone()) {
                    try {
                        Object result = ((Future<?>) r).get();  // << hangs here
                        result=null;
                    } catch (CancellationException ce) {
                        t = ce;
                    } catch (ExecutionException ee) {
                        t = ee.getCause();
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt(); // ignore/reset
                    }
                }
            }
            if (t != null) {
                System.out.println(t);
            }
        }
    }

    private static class FailTask implements Runnable {
        public void run(){
            System.out.println("something");
        }
    }
    public static void main(String[] args) {
        exec=Executors.newScheduledThreadPool(5);
        ScheduledExecutorService exec=new MyExecutor();
        exec.scheduleWithFixedDelay(new FailTask(), 0, 1000, TimeUnit.MILLISECONDS);
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :

A work around is to put a call to isDone() in the code before calling get as follows:


protected void afterExecute(Runnable r, Throwable t){
            super.afterExecute(r, t);
            if (t == null && r instanceof Future<?> && ((Future<?>)r).isDone()) {
                    try {
                        Object result = ((Future<?>) r).get()
                        result=null;
                    } catch (CancellationException ce) {
                        t = ce;
                    } catch (ExecutionException ee) {
                        t = ee.getCause();
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt(); // ignore/reset
                    }
                }
            }
            if (t != null) {
                System.out.println(t);
            }
        }

Comments
The sample code is being fixed as part of jsr166 jdk9 integration.
21-09-2015