Skip to content

Commit 7717644

Browse files
committed
Merge pull request androidannotations#624 from rom1v/cancel_background_fix
Serialized @background task cancellation bugfix
2 parents 77cbcea + 8d0c9f5 commit 7717644

1 file changed

Lines changed: 31 additions & 1 deletion

File tree

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/api/BackgroundExecutor.java‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.concurrent.Future;
2424
import java.util.concurrent.ScheduledExecutorService;
2525
import java.util.concurrent.TimeUnit;
26+
import java.util.concurrent.atomic.AtomicBoolean;
2627

2728
import android.util.Log;
2829

@@ -197,11 +198,21 @@ public static synchronized void cancelAll(String id, boolean mayInterruptIfRunni
197198
for (int i = tasks.size() - 1; i >= 0; i--) {
198199
Task task = tasks.get(i);
199200
if (id.equals(task.id)) {
200-
tasks.remove(i);
201201
if (task.future != null) {
202202
task.future.cancel(mayInterruptIfRunning);
203+
if (!task.managed.getAndSet(true)) {
204+
/*
205+
* the task has been submitted to the executor, but its
206+
* execution has not started yet, so that its run()
207+
* method will never call postExecute()
208+
*/
209+
task.postExecute();
210+
}
203211
} else if (task.executionAsked) {
204212
Log.w(TAG, "A task with id " + task.id + " cannot be cancelled (the executor set does not support it)");
213+
} else {
214+
/* this task has not been submitted to the executor */
215+
tasks.remove(i);
205216
}
206217
}
207218
}
@@ -252,6 +263,20 @@ public static abstract class Task implements Runnable {
252263
private boolean executionAsked;
253264
private Future<?> future;
254265

266+
/*
267+
* A task can be cancelled after it has been submitted to the executor
268+
* but before its run() method is called. In that case, run() will never
269+
* be called, hence neither will postExecute(): the tasks with the same
270+
* serial identifier (if any) will never be submitted.
271+
*
272+
* Therefore, cancelAll() *must* call postExecute() if run() is not
273+
* started.
274+
*
275+
* This flag guarantees that either cancelAll() or run() manages this
276+
* task post execution, but not both.
277+
*/
278+
private AtomicBoolean managed = new AtomicBoolean();
279+
255280
public Task(String id, int delay, String serial) {
256281
if (!"".equals(id)) {
257282
this.id = id;
@@ -267,6 +292,11 @@ public Task(String id, int delay, String serial) {
267292

268293
@Override
269294
public void run() {
295+
if (managed.getAndSet(true)) {
296+
/* cancelled and postExecute() already called */
297+
return;
298+
}
299+
270300
try {
271301
execute();
272302
} finally {

0 commit comments

Comments
 (0)