package com.glview.hwui.task; import com.glview.thread.Looper; import android.os.SystemClock; /** * Runs the specified task synchronously. * <p> * If the current thread is the same as the handler thread, then the runnable * runs immediately without being enqueued. Otherwise, posts the runnable * to the handler and waits for it to complete before returning. * </p><p> * This method is dangerous! Improper use can result in deadlocks. * Never call this method while any locks are held or use it in a * possibly re-entrant manner. * </p><p> * This method is occasionally useful in situations where a background thread * must synchronously await completion of a task that must run on the * handler's thread. However, this problem is often a symptom of bad design. * Consider improving the design (if possible) before resorting to this method. * </p><p> * One example of where you might want to use this method is when you just * set up a Handler thread and need to perform some initialization steps on * it before continuing execution. * </p><p> * If timeout occurs then this method returns <code>false</code> but the runnable * will remain posted on the handler and may already be in progress or * complete at a later time. * </p><p> * When using this method, be sure to use {@link Looper#quitSafely} when * quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely. * (TODO: We should fix this by making MessageQueue aware of blocking runnables.) * </p> * * @param task The Runnable that will be executed synchronously. * */ class BlockingRunable implements Runnable { // sometimes we store linked lists of these things /*package*/ BlockingRunable next; private static final Object sPoolSync = new Object(); private static BlockingRunable sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; private Task task; private boolean done; static BlockingRunable obtain() { synchronized (sPoolSync) { if (sPool != null) { BlockingRunable m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new BlockingRunable(); } public static BlockingRunable obtain(Task task) { if (task == null) { throw new IllegalArgumentException("task must not be null"); } BlockingRunable m = obtain(); m.done = false; m.task = task; return m; } /** * Return a Message instance to the global pool. * <p> * You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. * </p> */ public void recycle() { done = false; task = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } } @Override public void run() { try { task.run(); } finally { synchronized (this) { done = true; notifyAll(); } } } public boolean postAndWait(TaskHandler handler, long timeout) { if (!handler.post(this)) { return false; } return waitForTaskDone(timeout); } public boolean postAtFrontOfQueueAndWait(TaskHandler handler, long timeout) { if (!handler.postAtFrontOfQueue(this)) { return false; } return waitForTaskDone(timeout); } private boolean waitForTaskDone(long timeout) { synchronized (this) { if (timeout > 0) { final long expirationTime = SystemClock.uptimeMillis() + timeout; while (!done) { long delay = expirationTime - SystemClock.uptimeMillis(); if (delay <= 0) { return false; // timeout } try { wait(delay); } catch (InterruptedException ex) { } } } else { while (!done) { try { wait(); } catch (InterruptedException ex) { } } } } return true; } }