Merge "Fix change of behavior in Looper.quit()." into jb-mr2-dev

This commit is contained in:
Jeff Brown
2013-04-19 02:09:19 +00:00
committed by Android (Google) Code Review
6 changed files with 137 additions and 30 deletions

View File

@@ -17121,6 +17121,7 @@ package android.os {
method public int getThreadId();
method protected void onLooperPrepared();
method public boolean quit();
method public boolean quitSafely();
}
public abstract interface IBinder {
@@ -17161,6 +17162,7 @@ package android.os {
method public static void prepare();
method public static void prepareMainLooper();
method public void quit();
method public void quitSafely();
method public void setMessageLogging(android.util.Printer);
}

View File

@@ -413,27 +413,32 @@ public class Handler {
/**
* 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 r The Runnable that will be executed synchronously.
* @param timeout The timeout in milliseconds, or 0 to wait indefinitely.

View File

@@ -48,6 +48,7 @@ public class HandlerThread extends Thread {
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
@@ -83,12 +84,25 @@ public class HandlerThread extends Thread {
}
return mLooper;
}
/**
* Ask the currently running looper to quit. If the thread has not
* been started or has finished (that is if {@link #getLooper} returns
* null), then false is returned. Otherwise the looper is asked to
* quit and true is returned.
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
@@ -98,7 +112,34 @@ public class HandlerThread extends Thread {
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/

View File

@@ -202,18 +202,37 @@ public final class Looper {
/**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However delayed messages with due times in the future may not be handled before
* the loop terminates.
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after {@link #quit} has been called
* will fail. For example, the {@link Handler#sendMessage(Message)} method will
* return false when the looper is being terminated.
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit();
mQueue.quit(false);
}
/**
* Quits the looper safely.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p>
*/
public void quitSafely() {
mQueue.quit(true);
}
/**

View File

@@ -219,7 +219,7 @@ public final class MessageQueue {
}
}
void quit() {
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit.");
}
@@ -229,6 +229,12 @@ public final class MessageQueue {
return;
}
mQuiting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
}
nativeWake(mPtr);
}
@@ -473,4 +479,42 @@ public final class MessageQueue {
}
}
}
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycle();
p = n;
}
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycle();
} while (n != null);
}
}
}
}

View File

@@ -245,7 +245,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
if (mGLThread != null) {
// GLThread may still be running if this view was never
// attached to a window.
mGLThread.requestExitAndWait();
mGLThread.quitSafely();
}
} finally {
super.finalize();
@@ -628,7 +628,11 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
Log.d(TAG, "onDetachedFromWindow");
}
if (mGLThread != null) {
mGLThread.requestExitAndWait();
mGLThread.quitSafely();
try {
mGLThread.join();
} catch (InterruptedException ex) {
}
}
mDetached = true;
super.onDetachedFromWindow();
@@ -1627,14 +1631,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
mGLHandler.runWithScissors(mGetRenderModeRunnable, 0);
return mGetRenderModeRunnable.renderMode;
}
public void requestExitAndWait() {
getLooper().quit();
try {
this.join();
} catch (InterruptedException e) {
}
}
} // class GLThread
static class LogWriter extends Writer {