-
Bug
-
Resolution: Unresolved
-
P4
-
None
-
16
A DESCRIPTION OF THE PROBLEM :
Cancelled FutureTask's interrupt may leak
Use ScheduledThreadPoolExecutor to run period task.
A worker called W (according to Thread T1) in the ScheduledThreadPoolExecutor has got the FutureTask called T1, it is executing the `runAndReset()` method. It just run after the `s = state;` sentence, so the variable `s` equals `0` representing the `NEW` state. It is worth noting that the `runner = null;` sentence has not been executed because of instructions reorder.
At the moment, another Thread T2 invoke the `cancel(true)` method of T1, it continues to execute when the `UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)` sentence returns true until the `if (t != null)` sentence with the variable `Thread t` equals to `T1`, then it hangs.
W continues to run which passes the `handlePossibleCancellationInterrupt` sentence because of `if (s >= INTERRUPTING)` returning false and gets another FutureTask called FT2 after some time.
So if T2 interrupts the T1 at the time, FT2 may be affected.
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
protected boolean runAndReset() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
Cancelled FutureTask's interrupt may leak
Use ScheduledThreadPoolExecutor to run period task.
A worker called W (according to Thread T1) in the ScheduledThreadPoolExecutor has got the FutureTask called T1, it is executing the `runAndReset()` method. It just run after the `s = state;` sentence, so the variable `s` equals `0` representing the `NEW` state. It is worth noting that the `runner = null;` sentence has not been executed because of instructions reorder.
At the moment, another Thread T2 invoke the `cancel(true)` method of T1, it continues to execute when the `UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)` sentence returns true until the `if (t != null)` sentence with the variable `Thread t` equals to `T1`, then it hangs.
W continues to run which passes the `handlePossibleCancellationInterrupt` sentence because of `if (s >= INTERRUPTING)` returning false and gets another FutureTask called FT2 after some time.
So if T2 interrupts the T1 at the time, FT2 may be affected.
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
protected boolean runAndReset() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}