Workaround memory leaks in Looper
Message object from previous iteration may remain live, moving the iteration body into another function as a workaround. Bug: 146185876 Test: See b/146185876#comment1 for detailed steps Change-Id: I9239f0b8c862466f124690348ce69f31aaec2fb7
This commit is contained in:
@@ -94,6 +94,11 @@ public final class Looper {
|
||||
*/
|
||||
private long mSlowDeliveryThresholdMs;
|
||||
|
||||
/**
|
||||
* True if a message delivery takes longer than {@link #mSlowDeliveryThresholdMs}.
|
||||
*/
|
||||
private boolean mSlowDeliveryDetected;
|
||||
|
||||
/** Initialize the current thread as a looper.
|
||||
* This gives you a chance to create handlers that then reference
|
||||
* this looper, before actually starting the loop. Be sure to call
|
||||
@@ -147,6 +152,105 @@ public final class Looper {
|
||||
sObserver = observer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll and deliver single message, return true if the outer loop should continue.
|
||||
*/
|
||||
private static boolean loopOnce(final Looper me,
|
||||
final long ident, final int thresholdOverride) {
|
||||
Message msg = me.mQueue.next(); // might block
|
||||
if (msg == null) {
|
||||
// No message indicates that the message queue is quitting.
|
||||
return false;
|
||||
}
|
||||
|
||||
// This must be in a local variable, in case a UI event sets the logger
|
||||
final Printer logging = me.mLogging;
|
||||
if (logging != null) {
|
||||
logging.println(">>>>> Dispatching to " + msg.target + " "
|
||||
+ msg.callback + ": " + msg.what);
|
||||
}
|
||||
// Make sure the observer won't change while processing a transaction.
|
||||
final Observer observer = sObserver;
|
||||
|
||||
final long traceTag = me.mTraceTag;
|
||||
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
|
||||
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
|
||||
if (thresholdOverride > 0) {
|
||||
slowDispatchThresholdMs = thresholdOverride;
|
||||
slowDeliveryThresholdMs = thresholdOverride;
|
||||
}
|
||||
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
|
||||
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
|
||||
|
||||
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
|
||||
final boolean needEndTime = logSlowDispatch;
|
||||
|
||||
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
|
||||
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
|
||||
}
|
||||
|
||||
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
|
||||
final long dispatchEnd;
|
||||
Object token = null;
|
||||
if (observer != null) {
|
||||
token = observer.messageDispatchStarting();
|
||||
}
|
||||
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
|
||||
try {
|
||||
msg.target.dispatchMessage(msg);
|
||||
if (observer != null) {
|
||||
observer.messageDispatched(token, msg);
|
||||
}
|
||||
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
|
||||
} catch (Exception exception) {
|
||||
if (observer != null) {
|
||||
observer.dispatchingThrewException(token, msg, exception);
|
||||
}
|
||||
throw exception;
|
||||
} finally {
|
||||
ThreadLocalWorkSource.restore(origWorkSource);
|
||||
if (traceTag != 0) {
|
||||
Trace.traceEnd(traceTag);
|
||||
}
|
||||
}
|
||||
if (logSlowDelivery) {
|
||||
if (me.mSlowDeliveryDetected) {
|
||||
if ((dispatchStart - msg.when) <= 10) {
|
||||
Slog.w(TAG, "Drained");
|
||||
me.mSlowDeliveryDetected = false;
|
||||
}
|
||||
} else {
|
||||
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
|
||||
msg)) {
|
||||
// Once we write a slow delivery log, suppress until the queue drains.
|
||||
me.mSlowDeliveryDetected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logSlowDispatch) {
|
||||
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
|
||||
}
|
||||
|
||||
if (logging != null) {
|
||||
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
|
||||
}
|
||||
|
||||
// Make sure that during the course of dispatching the
|
||||
// identity of the thread wasn't corrupted.
|
||||
final long newIdent = Binder.clearCallingIdentity();
|
||||
if (ident != newIdent) {
|
||||
Log.wtf(TAG, "Thread identity changed from 0x"
|
||||
+ Long.toHexString(ident) + " to 0x"
|
||||
+ Long.toHexString(newIdent) + " while dispatching to "
|
||||
+ msg.target.getClass().getName() + " "
|
||||
+ msg.callback + " what=" + msg.what);
|
||||
}
|
||||
|
||||
msg.recycleUnchecked();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the message queue in this thread. Be sure to call
|
||||
* {@link #quit()} to end the loop.
|
||||
@@ -162,7 +266,6 @@ public final class Looper {
|
||||
}
|
||||
|
||||
me.mInLoop = true;
|
||||
final MessageQueue queue = me.mQueue;
|
||||
|
||||
// Make sure the identity of this thread is that of the local process,
|
||||
// and keep track of what that identity token actually is.
|
||||
@@ -177,99 +280,12 @@ public final class Looper {
|
||||
+ Thread.currentThread().getName()
|
||||
+ ".slow", 0);
|
||||
|
||||
boolean slowDeliveryDetected = false;
|
||||
me.mSlowDeliveryDetected = false;
|
||||
|
||||
for (;;) {
|
||||
Message msg = queue.next(); // might block
|
||||
if (msg == null) {
|
||||
// No message indicates that the message queue is quitting.
|
||||
if (!loopOnce(me, ident, thresholdOverride)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This must be in a local variable, in case a UI event sets the logger
|
||||
final Printer logging = me.mLogging;
|
||||
if (logging != null) {
|
||||
logging.println(">>>>> Dispatching to " + msg.target + " " +
|
||||
msg.callback + ": " + msg.what);
|
||||
}
|
||||
// Make sure the observer won't change while processing a transaction.
|
||||
final Observer observer = sObserver;
|
||||
|
||||
final long traceTag = me.mTraceTag;
|
||||
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
|
||||
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
|
||||
if (thresholdOverride > 0) {
|
||||
slowDispatchThresholdMs = thresholdOverride;
|
||||
slowDeliveryThresholdMs = thresholdOverride;
|
||||
}
|
||||
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
|
||||
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
|
||||
|
||||
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
|
||||
final boolean needEndTime = logSlowDispatch;
|
||||
|
||||
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
|
||||
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
|
||||
}
|
||||
|
||||
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
|
||||
final long dispatchEnd;
|
||||
Object token = null;
|
||||
if (observer != null) {
|
||||
token = observer.messageDispatchStarting();
|
||||
}
|
||||
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
|
||||
try {
|
||||
msg.target.dispatchMessage(msg);
|
||||
if (observer != null) {
|
||||
observer.messageDispatched(token, msg);
|
||||
}
|
||||
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
|
||||
} catch (Exception exception) {
|
||||
if (observer != null) {
|
||||
observer.dispatchingThrewException(token, msg, exception);
|
||||
}
|
||||
throw exception;
|
||||
} finally {
|
||||
ThreadLocalWorkSource.restore(origWorkSource);
|
||||
if (traceTag != 0) {
|
||||
Trace.traceEnd(traceTag);
|
||||
}
|
||||
}
|
||||
if (logSlowDelivery) {
|
||||
if (slowDeliveryDetected) {
|
||||
if ((dispatchStart - msg.when) <= 10) {
|
||||
Slog.w(TAG, "Drained");
|
||||
slowDeliveryDetected = false;
|
||||
}
|
||||
} else {
|
||||
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
|
||||
msg)) {
|
||||
// Once we write a slow delivery log, suppress until the queue drains.
|
||||
slowDeliveryDetected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logSlowDispatch) {
|
||||
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
|
||||
}
|
||||
|
||||
if (logging != null) {
|
||||
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
|
||||
}
|
||||
|
||||
// Make sure that during the course of dispatching the
|
||||
// identity of the thread wasn't corrupted.
|
||||
final long newIdent = Binder.clearCallingIdentity();
|
||||
if (ident != newIdent) {
|
||||
Log.wtf(TAG, "Thread identity changed from 0x"
|
||||
+ Long.toHexString(ident) + " to 0x"
|
||||
+ Long.toHexString(newIdent) + " while dispatching to "
|
||||
+ msg.target.getClass().getName() + " "
|
||||
+ msg.callback + " what=" + msg.what);
|
||||
}
|
||||
|
||||
msg.recycleUnchecked();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user