View Javadoc
1   /*
2    * Copyright (C) 2015 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.util.concurrent;
16  
17  import com.google.common.annotations.GwtCompatible;
18  import com.google.j2objc.annotations.ReflectionSupport;
19  import java.util.concurrent.atomic.AtomicReference;
20  import javax.annotation.Nullable;
21  
22  @GwtCompatible(emulated = true)
23  @ReflectionSupport(value = ReflectionSupport.Level.FULL)
24  // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause
25  // getDeclaredField to throw a NoSuchFieldException when the field is definitely there.
26  // Since this class only needs CAS on one field, we can avoid this bug by extending AtomicReference
27  // instead of using an AtomicReferenceFieldUpdater. This reference stores Thread instances
28  // and DONE/INTERRUPTED - they have a common ancestor of Runnable.
29  abstract class InterruptibleTask<T> extends AtomicReference<Runnable> implements Runnable {
30    private static final class DoNothingRunnable implements Runnable {
31      @Override
32      public void run() {}
33    }
34    // The thread executing the task publishes itself to the superclass' reference and the thread
35    // interrupting sets DONE when it has finished interrupting.
36    private static final Runnable DONE = new DoNothingRunnable();
37    private static final Runnable INTERRUPTING = new DoNothingRunnable();
38  
39    @Override
40    public final void run() {
41      /*
42       * Set runner thread before checking isDone(). If we were to check isDone() first, the task
43       * might be cancelled before we set the runner thread. That would make it impossible to
44       * interrupt, yet it will still run, since interruptTask will leave the runner value null,
45       * allowing the CAS below to succeed.
46       */
47      Thread currentThread = Thread.currentThread();
48      if (!compareAndSet(null, currentThread)) {
49        return; // someone else has run or is running.
50      }
51      
52      boolean run = !isDone();
53      T result = null;
54      Throwable error = null;
55      try {
56        if (run) {
57          result = runInterruptibly();
58        }
59      } catch (Throwable t) {
60        error = t;
61      } finally {
62        // Attempt to set the task as done so that further attempts to interrupt will fail.
63        if (!compareAndSet(currentThread, DONE)) {
64          // If we were interrupted, it is possible that the interrupted bit hasn't been set yet. Wait
65          // for the interrupting thread to set DONE. See interruptTask().
66          // We want to wait so that we don't interrupt the _next_ thing run on the thread.
67          // Note: We don't reset the interrupted bit, just wait for it to be set.
68          // If this is a thread pool thread, the thread pool will reset it for us. Otherwise, the
69          // interrupted bit may have been intended for something else, so don't clear it.
70          while (get() == INTERRUPTING) {
71            Thread.yield();
72          }
73          /*
74           * TODO(cpovirk): Clear interrupt status here? We currently don't, which means that an
75           * interrupt before, during, or after runInterruptibly() (unless it produced an
76           * InterruptedException caught above) can linger and affect listeners.
77           */
78        }
79        if (run) {
80          afterRanInterruptibly(result, error);
81        }
82      }
83    }
84  
85    /**
86     * Called before runInterruptibly - if true, runInterruptibly and afterRanInterruptibly will not
87     * be called.
88     */
89    abstract boolean isDone();
90  
91    /**
92     * Do interruptible work here - do not complete Futures here, as their listeners could be
93     * interrupted.
94     */
95    abstract T runInterruptibly() throws Exception;
96  
97    /**
98     * Any interruption that happens as a result of calling interruptTask will arrive before this
99     * method is called. Complete Futures here.
100    */
101   abstract void afterRanInterruptibly(@Nullable T result, @Nullable Throwable error);
102 
103   final void interruptTask() {
104     // Since the Thread is replaced by DONE before run() invokes listeners or returns, if we succeed
105     // in this CAS, there's no risk of interrupting the wrong thread or interrupting a thread that
106     // isn't currently executing this task.
107     Runnable currentRunner = get();
108     if (currentRunner instanceof Thread && compareAndSet(currentRunner, INTERRUPTING)) {
109       ((Thread) currentRunner).interrupt();
110       set(DONE);
111     }
112   }
113 
114   @Override
115   public abstract String toString();
116 }