Merge changes Ibd70ef40,Ibcb36ebf
* changes: Don't let offload broadcasts to be starved. Don't wait for message queues to become idle in wait-for-barrier.
This commit is contained in:
committed by
Android (Google) Code Review
commit
1634476633
@@ -18324,7 +18324,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
|
||||
public void waitForBroadcastBarrier(@Nullable PrintWriter pw) {
|
||||
enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
|
||||
BroadcastLoopers.waitForIdle(pw);
|
||||
BroadcastLoopers.waitForBarrier(pw);
|
||||
for (BroadcastQueue queue : mBroadcastQueues) {
|
||||
queue.waitForBarrier(pw);
|
||||
}
|
||||
|
||||
@@ -153,11 +153,26 @@ public class BroadcastConstants {
|
||||
"bcast_extra_running_urgent_process_queues";
|
||||
private static final int DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES = 1;
|
||||
|
||||
/**
|
||||
* For {@link BroadcastQueueModernImpl}: Maximum number of consecutive urgent
|
||||
* broadcast dispatches allowed before letting broadcasts in lower priority queue
|
||||
* to be scheduled in order to avoid starvation.
|
||||
*/
|
||||
public int MAX_CONSECUTIVE_URGENT_DISPATCHES = DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES;
|
||||
private static final String KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES =
|
||||
"bcast_max_consecutive_urgent_dispatches";
|
||||
private static final int DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES = 3;
|
||||
|
||||
/**
|
||||
* For {@link BroadcastQueueModernImpl}: Maximum number of consecutive normal
|
||||
* broadcast dispatches allowed before letting broadcasts in lower priority queue
|
||||
* to be scheduled in order to avoid starvation.
|
||||
*/
|
||||
public int MAX_CONSECUTIVE_NORMAL_DISPATCHES = DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES;
|
||||
private static final String KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES =
|
||||
"bcast_max_consecutive_normal_dispatches";
|
||||
private static final int DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES = 10;
|
||||
|
||||
/**
|
||||
* For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts
|
||||
* to dispatch to a "running" process queue before we retire them back to
|
||||
@@ -341,6 +356,9 @@ public class BroadcastConstants {
|
||||
MAX_CONSECUTIVE_URGENT_DISPATCHES = getDeviceConfigInt(
|
||||
KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES,
|
||||
DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES);
|
||||
MAX_CONSECUTIVE_NORMAL_DISPATCHES = getDeviceConfigInt(
|
||||
KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES,
|
||||
DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES);
|
||||
MAX_RUNNING_ACTIVE_BROADCASTS = getDeviceConfigInt(KEY_MAX_RUNNING_ACTIVE_BROADCASTS,
|
||||
DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS);
|
||||
MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS,
|
||||
@@ -396,6 +414,10 @@ public class BroadcastConstants {
|
||||
TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println();
|
||||
pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).println();
|
||||
pw.print(KEY_MAX_HISTORY_SUMMARY_SIZE, MAX_HISTORY_SUMMARY_SIZE).println();
|
||||
pw.print(KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES,
|
||||
MAX_CONSECUTIVE_URGENT_DISPATCHES).println();
|
||||
pw.print(KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES,
|
||||
MAX_CONSECUTIVE_NORMAL_DISPATCHES).println();
|
||||
pw.decreaseIndent();
|
||||
pw.println();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.am;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.MessageQueue;
|
||||
@@ -30,6 +31,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Collection of {@link Looper} that are known to be used for broadcast dispatch
|
||||
@@ -73,19 +75,44 @@ public class BroadcastLoopers {
|
||||
* still in the future are ignored for the purposes of the idle test.
|
||||
*/
|
||||
public static void waitForIdle(@Nullable PrintWriter pw) {
|
||||
waitForCondition(pw, (looper, latch) -> {
|
||||
final MessageQueue queue = looper.getQueue();
|
||||
queue.addIdleHandler(() -> {
|
||||
latch.countDown();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for all registered {@link Looper} instances to handle currently waiting messages.
|
||||
* Note that {@link Message#when} still in the future are ignored for the purposes
|
||||
* of the idle test.
|
||||
*/
|
||||
public static void waitForBarrier(@Nullable PrintWriter pw) {
|
||||
waitForCondition(pw, (looper, latch) -> {
|
||||
(new Handler(looper)).post(() -> {
|
||||
latch.countDown();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for all registered {@link Looper} instances to meet a certain condition.
|
||||
*/
|
||||
private static void waitForCondition(@Nullable PrintWriter pw,
|
||||
@NonNull BiConsumer<Looper, CountDownLatch> condition) {
|
||||
final CountDownLatch latch;
|
||||
synchronized (sLoopers) {
|
||||
final int N = sLoopers.size();
|
||||
latch = new CountDownLatch(N);
|
||||
for (int i = 0; i < N; i++) {
|
||||
final MessageQueue queue = sLoopers.valueAt(i).getQueue();
|
||||
final Looper looper = sLoopers.valueAt(i);
|
||||
final MessageQueue queue = looper.getQueue();
|
||||
if (queue.isIdle()) {
|
||||
latch.countDown();
|
||||
} else {
|
||||
queue.addIdleHandler(() -> {
|
||||
latch.countDown();
|
||||
return false;
|
||||
});
|
||||
condition.accept(looper, latch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,12 @@ class BroadcastProcessQueue {
|
||||
*/
|
||||
private int mActiveCountConsecutiveUrgent;
|
||||
|
||||
/**
|
||||
* Number of consecutive normal broadcasts that have been dispatched
|
||||
* since the last offload dispatch.
|
||||
*/
|
||||
private int mActiveCountConsecutiveNormal;
|
||||
|
||||
/**
|
||||
* Count of pending broadcasts of these various flavors.
|
||||
*/
|
||||
@@ -551,48 +557,63 @@ class BroadcastProcessQueue {
|
||||
* Will thrown an exception if there are no pending broadcasts; relies on
|
||||
* {@link #isEmpty()} being false.
|
||||
*/
|
||||
SomeArgs removeNextBroadcast() {
|
||||
private @Nullable SomeArgs removeNextBroadcast() {
|
||||
final ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
|
||||
if (queue == mPendingUrgent) {
|
||||
mActiveCountConsecutiveUrgent++;
|
||||
} else {
|
||||
} else if (queue == mPending) {
|
||||
mActiveCountConsecutiveUrgent = 0;
|
||||
mActiveCountConsecutiveNormal++;
|
||||
} else if (queue == mPendingOffload) {
|
||||
mActiveCountConsecutiveUrgent = 0;
|
||||
mActiveCountConsecutiveNormal = 0;
|
||||
}
|
||||
return queue.removeFirst();
|
||||
return !isQueueEmpty(queue) ? queue.removeFirst() : null;
|
||||
}
|
||||
|
||||
@Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() {
|
||||
ArrayDeque<SomeArgs> nextUrgent = mPendingUrgent.isEmpty() ? null : mPendingUrgent;
|
||||
ArrayDeque<SomeArgs> nextNormal = null;
|
||||
if (!mPending.isEmpty()) {
|
||||
nextNormal = mPending;
|
||||
} else if (!mPendingOffload.isEmpty()) {
|
||||
nextNormal = mPendingOffload;
|
||||
final ArrayDeque<SomeArgs> nextNormal = queueForNextBroadcast(
|
||||
mPending, mPendingOffload,
|
||||
mActiveCountConsecutiveNormal, constants.MAX_CONSECUTIVE_NORMAL_DISPATCHES);
|
||||
final ArrayDeque<SomeArgs> nextBroadcastQueue = queueForNextBroadcast(
|
||||
mPendingUrgent, nextNormal,
|
||||
mActiveCountConsecutiveUrgent, constants.MAX_CONSECUTIVE_URGENT_DISPATCHES);
|
||||
return nextBroadcastQueue;
|
||||
}
|
||||
|
||||
private @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast(
|
||||
@Nullable ArrayDeque<SomeArgs> highPriorityQueue,
|
||||
@Nullable ArrayDeque<SomeArgs> lowPriorityQueue,
|
||||
int consecutiveHighPriorityCount,
|
||||
int maxHighPriorityDispatchLimit) {
|
||||
// nothing high priority pending, no further decisionmaking
|
||||
if (isQueueEmpty(highPriorityQueue)) {
|
||||
return lowPriorityQueue;
|
||||
}
|
||||
// nothing urgent pending, no further decisionmaking
|
||||
if (nextUrgent == null) {
|
||||
return nextNormal;
|
||||
}
|
||||
// nothing but urgent pending, also no further decisionmaking
|
||||
if (nextNormal == null) {
|
||||
return nextUrgent;
|
||||
// nothing but high priority pending, also no further decisionmaking
|
||||
if (isQueueEmpty(lowPriorityQueue)) {
|
||||
return highPriorityQueue;
|
||||
}
|
||||
|
||||
// Starvation mitigation: although we prioritize urgent broadcasts by default,
|
||||
// we allow non-urgent deliveries to make steady progress even if urgent
|
||||
// broadcasts are arriving faster than they can be dispatched.
|
||||
// Starvation mitigation: although we prioritize high priority queues by default,
|
||||
// we allow low priority queues to make steady progress even if broadcasts in
|
||||
// high priority queue are arriving faster than they can be dispatched.
|
||||
//
|
||||
// We do not try to defer to the next non-urgent broadcast if that broadcast
|
||||
// We do not try to defer to the next broadcast in low priority queues if that broadcast
|
||||
// is ordered and still blocked on delivery to other recipients.
|
||||
final SomeArgs nextNormalArgs = nextNormal.peekFirst();
|
||||
final BroadcastRecord rNormal = (BroadcastRecord) nextNormalArgs.arg1;
|
||||
final int nextNormalIndex = nextNormalArgs.argi1;
|
||||
final BroadcastRecord rUrgent = (BroadcastRecord) nextUrgent.peekFirst().arg1;
|
||||
final boolean canTakeNormal =
|
||||
mActiveCountConsecutiveUrgent >= constants.MAX_CONSECUTIVE_URGENT_DISPATCHES
|
||||
&& rNormal.enqueueTime <= rUrgent.enqueueTime
|
||||
&& !blockedOnOrderedDispatch(rNormal, nextNormalIndex);
|
||||
return canTakeNormal ? nextNormal : nextUrgent;
|
||||
final SomeArgs nextLPArgs = lowPriorityQueue.peekFirst();
|
||||
final BroadcastRecord nextLPRecord = (BroadcastRecord) nextLPArgs.arg1;
|
||||
final int nextLPRecordIndex = nextLPArgs.argi1;
|
||||
final BroadcastRecord nextHPRecord = (BroadcastRecord) highPriorityQueue.peekFirst().arg1;
|
||||
final boolean isLPQueueEligible =
|
||||
consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit
|
||||
&& nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime
|
||||
&& !blockedOnOrderedDispatch(nextLPRecord, nextLPRecordIndex);
|
||||
return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue;
|
||||
}
|
||||
|
||||
private static boolean isQueueEmpty(@Nullable ArrayDeque<SomeArgs> queue) {
|
||||
return (queue == null || queue.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -600,13 +621,13 @@ class BroadcastProcessQueue {
|
||||
*/
|
||||
@Nullable SomeArgs peekNextBroadcast() {
|
||||
ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
|
||||
return (queue != null) ? queue.peekFirst() : null;
|
||||
return !isQueueEmpty(queue) ? queue.peekFirst() : null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable BroadcastRecord peekNextBroadcastRecord() {
|
||||
ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
|
||||
return (queue != null) ? (BroadcastRecord) queue.peekFirst().arg1 : null;
|
||||
return !isQueueEmpty(queue) ? (BroadcastRecord) queue.peekFirst().arg1 : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -591,6 +591,73 @@ public class BroadcastQueueModernImplTest {
|
||||
assertEquals(Intent.ACTION_ALARM_CHANGED, queue.getActive().intent.getAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that offload broadcasts are not starved because of broadcasts in higher priority
|
||||
* queues.
|
||||
*/
|
||||
@Test
|
||||
public void testOffloadStarvation() {
|
||||
final BroadcastOptions optInteractive = BroadcastOptions.makeBasic();
|
||||
optInteractive.setInteractive(true);
|
||||
|
||||
mConstants.MAX_CONSECUTIVE_URGENT_DISPATCHES = 1;
|
||||
mConstants.MAX_CONSECUTIVE_NORMAL_DISPATCHES = 2;
|
||||
final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
|
||||
PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
|
||||
|
||||
// mix of broadcasts, with more than 2 normal
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_BOOT_COMPLETED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_PACKAGE_CHANGED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_ALARM_CHANGED)), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_APPLICATION_PREFERENCES),
|
||||
optInteractive), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE),
|
||||
optInteractive), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_INPUT_METHOD_CHANGED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_NEW_OUTGOING_CALL),
|
||||
optInteractive), 0);
|
||||
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_LOCALE_CHANGED, queue.getActive().intent.getAction());
|
||||
// after MAX_CONSECUTIVE_URGENT_DISPATCHES expect an ordinary one next
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_TIMEZONE_CHANGED, queue.getActive().intent.getAction());
|
||||
// and then back to prioritizing urgent ones
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_APPLICATION_PREFERENCES, queue.getActive().intent.getAction());
|
||||
// after MAX_CONSECUTIVE_URGENT_DISPATCHES, again an ordinary one next
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_ALARM_CHANGED, queue.getActive().intent.getAction());
|
||||
// and then back to prioritizing urgent ones
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(AppWidgetManager.ACTION_APPWIDGET_UPDATE,
|
||||
queue.getActive().intent.getAction());
|
||||
// after MAX_CONSECUTIVE_URGENT_DISPATCHES and MAX_CONSECUTIVE_NORMAL_DISPATCHES,
|
||||
// expect an offload one
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_BOOT_COMPLETED, queue.getActive().intent.getAction());
|
||||
// and then back to prioritizing urgent ones
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_INPUT_METHOD_CHANGED, queue.getActive().intent.getAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that sending a broadcast that removes any matching pending
|
||||
* broadcasts is applied as expected.
|
||||
|
||||
Reference in New Issue
Block a user