2323import java .util .concurrent .Future ;
2424import java .util .concurrent .ScheduledExecutorService ;
2525import java .util .concurrent .TimeUnit ;
26+ import java .util .concurrent .atomic .AtomicBoolean ;
2627
2728import 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