| 22 | |
| 23 | |
| 24 | @GwtCompatible(emulated = true) |
| 25 | abstract class InterruptibleTask implements Runnable { |
| 26 | // These two fields are used to interrupt running tasks. The thread executing the task publishes |
| 27 | // itself to the 'runner' field and the thread interrupting sets 'doneInterrupting' when it has |
| 28 | // finished interrupting. |
| 29 | private volatile Thread runner; |
| 30 | private volatile boolean doneInterrupting; |
| 31 | private static final AtomicHelper ATOMIC_HELPER; |
| 32 | private static final Logger log = Logger.getLogger(InterruptibleTask.class.getName()); |
| 33 | |
| 34 | static { |
| 35 | AtomicHelper helper; |
| 36 | try { |
| 37 | helper = new SafeAtomicHelper(newUpdater(InterruptibleTask.class, (Class) Thread.class, "runner")); |
| 38 | } catch (Throwable reflectionFailure) { |
| 39 | // Some Android 5.0.x Samsung devices have bugs in JDK reflection APIs that cause |
| 40 | // getDeclaredField to throw a NoSuchFieldException when the field is definitely there. |
| 41 | // For these users fallback to a suboptimal implementation, based on synchronized. This will |
| 42 | // be a definite performance hit to those users. |
| 43 | log.log(Level.SEVERE, "SafeAtomicHelper is broken!", reflectionFailure); |
| 44 | helper = new SynchronizedAtomicHelper(); |
| 45 | } |
| 46 | ATOMIC_HELPER = helper; |
| 47 | } |
| 48 | |
| 49 | @Override |
| 50 | public final void run() { |
| 51 | if (!ATOMIC_HELPER.compareAndSetRunner(this, null, Thread.currentThread())) { |
| 52 | return; // someone else has run or is running. |
| 53 | } |
| 54 | try { |
| 55 | runInterruptibly(); |
| 56 | } finally { |
| 57 | if (wasInterrupted()) { |
| 58 | // We were interrupted, it is possible that the interrupted bit hasn't been set yet. Wait |
| 59 | // for the interrupting thread to set 'doneInterrupting' to true. See interruptTask(). |
| 60 | // We want to wait so that we don't interrupt the _next_ thing run on the thread. |
| 61 | // Note: We don't reset the interrupted bit, just wait for it to be set. |
| 62 | // If this is a thread pool thread, the thread pool will reset it for us. Otherwise, the |
| 63 | // interrupted bit may have been intended for something else, so don't clear it. |
| 64 | while (!doneInterrupting) { |
| 65 | Thread.yield(); |
| 66 | } |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | abstract void runInterruptibly(); |
| 72 | |
| 73 | abstract boolean wasInterrupted(); |
| 74 | final void interruptTask() { |
| 75 | // interruptTask is guaranteed to be called at most once, and if runner is non-null when that |
| 76 | // happens, then it must have been the first thread that entered run(). So there is no risk that |
| 77 | // we are interrupting the wrong thread. |
| 78 | Thread currentRunner = runner; |
| 79 | if (currentRunner != null) { |
| 80 | currentRunner.interrupt(); |
| 81 | } |