Reserve execution slots for expedited jobs.

Make sure we reserve at least 3 slots for expedited jobs (in the ideal
case) so we can get them to run as quickly as possible.

Also fix how we assign slots when there are already running jobs.

Bug: 171305774
Test: atest CtsJobSchedulerTestCases
Test: atest FrameworksMockingServicesTests:JobSchedulerServiceTest
Test: atest FrameworksServicesTests:PrioritySchedulingTest
Test: atest FrameworksServicesTests:WorkCountTrackerTest
Test: atest FrameworksServicesTests:WorkTypeConfigTest
Change-Id: Ic08bc9d665c4c016f01ddce8f952710e72bd27f9
This commit is contained in:
Kweku Adams
2021-02-01 11:09:25 -08:00
parent 076669f905
commit 20b0eee64f
4 changed files with 503 additions and 319 deletions

View File

@@ -75,13 +75,16 @@ class JobConcurrencyManager {
// Try to give higher priority types lower values.
static final int WORK_TYPE_NONE = 0;
static final int WORK_TYPE_TOP = 1 << 0;
static final int WORK_TYPE_BG = 1 << 1;
static final int WORK_TYPE_BGUSER = 1 << 2;
private static final int NUM_WORK_TYPES = 3;
static final int WORK_TYPE_EJ = 1 << 1;
static final int WORK_TYPE_BG = 1 << 2;
static final int WORK_TYPE_BGUSER = 1 << 3;
@VisibleForTesting
static final int NUM_WORK_TYPES = 4;
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
WORK_TYPE_NONE,
WORK_TYPE_TOP,
WORK_TYPE_EJ,
WORK_TYPE_BG,
WORK_TYPE_BGUSER
})
@@ -106,54 +109,60 @@ class JobConcurrencyManager {
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
new WorkConfigLimitsPerMemoryTrimLevel(
new WorkTypeConfig("screen_on_normal", 8,
new WorkTypeConfig("screen_on_normal", 11,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3),
Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
),
new WorkTypeConfig("screen_on_moderate", 8,
new WorkTypeConfig("screen_on_moderate", 9,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
),
new WorkTypeConfig("screen_on_low", 5,
new WorkTypeConfig("screen_on_low", 6,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1),
Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
),
new WorkTypeConfig("screen_on_critical", 5,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
)
);
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
new WorkConfigLimitsPerMemoryTrimLevel(
new WorkTypeConfig("screen_off_normal", 10,
new WorkTypeConfig("screen_off_normal", 13,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
),
new WorkTypeConfig("screen_off_moderate", 10,
new WorkTypeConfig("screen_off_moderate", 13,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3),
Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
),
new WorkTypeConfig("screen_off_low", 5,
new WorkTypeConfig("screen_off_low", 7,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
),
new WorkTypeConfig("screen_off_critical", 5,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
)
@@ -818,15 +827,22 @@ class JobConcurrencyManager {
int getJobWorkTypes(@NonNull JobStatus js) {
int classification = 0;
// TODO(171305774): create dedicated work type for EJ and FGS
if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
|| js.shouldTreatAsExpeditedJob()) {
classification |= WORK_TYPE_TOP;
} else if (shouldRunAsFgUserJob(js)) {
classification |= WORK_TYPE_BG;
// TODO: create dedicated work type for FGS
if (shouldRunAsFgUserJob(js)) {
if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
classification |= WORK_TYPE_TOP;
} else {
classification |= WORK_TYPE_BG;
}
if (js.shouldTreatAsExpeditedJob()) {
classification |= WORK_TYPE_EJ;
}
} else {
// TODO(171305774): create dedicated slots for EJs of bg user
classification |= WORK_TYPE_BGUSER;
}
return classification;
}
@@ -835,10 +851,12 @@ class JobConcurrencyManager {
private static final String KEY_PREFIX_MAX_TOTAL =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
private static final String KEY_PREFIX_MAX_BGUSER =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
private static final String KEY_PREFIX_MIN_BGUSER =
CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
@@ -885,6 +903,10 @@ class JobConcurrencyManager {
properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
final int maxEj = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_EJ, maxEj);
final int maxBg = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
@@ -902,6 +924,12 @@ class JobConcurrencyManager {
mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
remaining -= minTop;
// Ensure ej is in the range [0, min(maxEj, remaining)]
final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining),
properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier,
mDefaultMinReservedSlots.get(WORK_TYPE_EJ))));
mMinReservedSlots.put(WORK_TYPE_EJ, minEj);
remaining -= minEj;
// Ensure bg is in the range [0, min(maxBg, remaining)]
final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining),
properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
@@ -933,6 +961,10 @@ class JobConcurrencyManager {
.println();
pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
.println();
pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ))
.println();
pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ))
.println();
pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG))
.println();
pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
@@ -1032,24 +1064,21 @@ class JobConcurrencyManager {
private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES);
private int mNumUnspecialized = 0;
private int mNumUnspecializedRemaining = 0;
void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
mConfigMaxTotal = workTypeConfig.getMaxTotal();
mConfigNumReservedSlots.put(WORK_TYPE_TOP,
workTypeConfig.getMinReserved(WORK_TYPE_TOP));
mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
mNumUnspecialized = mConfigMaxTotal;
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BGUSER);
mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
@@ -1078,6 +1107,9 @@ class JobConcurrencyManager {
if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
}
if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + 1);
}
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
}
@@ -1165,61 +1197,70 @@ class JobConcurrencyManager {
void onCountDone() {
// Calculate how many slots to reserve for each work type. "Unspecialized" slots will
// be reserved for higher importance types first (ie. top before bg).
mNumUnspecialized = mConfigMaxTotal;
final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
+ mNumPendingJobs.get(WORK_TYPE_TOP);
int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
mNumUnspecialized -= resTop;
final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
mNumUnspecialized -= resBg;
final int numBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER)
+ mNumPendingJobs.get(WORK_TYPE_BGUSER);
int resBgUser = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BGUSER), numBgUser);
mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser);
mNumUnspecialized -= resBgUser;
// be reserved for higher importance types first (ie. top before ej before bg).
// Steps:
// 1. Account for slots for already running jobs
// 2. Use remaining unaccounted slots to try and ensure minimum reserved slots
// 3. Allocate remaining up to max, based on importance
mNumUnspecializedRemaining = mNumUnspecialized;
// Account for already running jobs after we've assigned the minimum number of slots.
int unspecializedAssigned;
int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
if (extraRunning > 0) {
unspecializedAssigned = Math.max(0,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
extraRunning));
resTop += unspecializedAssigned;
mNumUnspecializedRemaining -= extraRunning;
}
extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
if (extraRunning > 0) {
unspecializedAssigned = Math.max(0,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
resBg += unspecializedAssigned;
mNumUnspecializedRemaining -= extraRunning;
}
extraRunning = (mNumRunningJobs.get(WORK_TYPE_BGUSER) - resBgUser);
if (extraRunning > 0) {
unspecializedAssigned = Math.max(0,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER) - resBgUser,
extraRunning));
resBgUser += unspecializedAssigned;
mNumUnspecializedRemaining -= extraRunning;
}
mNumUnspecializedRemaining = mConfigMaxTotal;
// Assign remaining unspecialized based on ranking.
unspecializedAssigned = Math.max(0,
// Step 1
int runTop = mNumRunningJobs.get(WORK_TYPE_TOP);
int resTop = runTop;
mNumUnspecializedRemaining -= resTop;
int runEj = mNumRunningJobs.get(WORK_TYPE_EJ);
int resEj = runEj;
mNumUnspecializedRemaining -= resEj;
int runBg = mNumRunningJobs.get(WORK_TYPE_BG);
int resBg = runBg;
mNumUnspecializedRemaining -= resBg;
int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER);
int resBgUser = runBgUser;
mNumUnspecializedRemaining -= resBgUser;
// Step 2
final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP);
int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop)));
resTop += fillUp;
mNumUnspecializedRemaining -= fillUp;
final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ);
fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj)));
resEj += fillUp;
mNumUnspecializedRemaining -= fillUp;
final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG);
fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg)));
resBg += fillUp;
mNumUnspecializedRemaining -= fillUp;
final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER);
fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
Math.min(numBgUser,
mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser)));
resBgUser += fillUp;
mNumUnspecializedRemaining -= fillUp;
// Step 3
int unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj));
mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser)
@@ -1238,6 +1279,15 @@ class JobConcurrencyManager {
return WORK_TYPE_TOP;
}
}
if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
final int maxAllowed = Math.min(
mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ),
mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining);
if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ)
< maxAllowed) {
return WORK_TYPE_EJ;
}
}
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
final int maxAllowed = Math.min(
mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),

View File

@@ -62,6 +62,7 @@ import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
import com.android.server.SystemServiceManager;
import com.android.server.job.controllers.JobStatus;
import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import org.junit.After;
@@ -128,6 +129,9 @@ public class JobSchedulerServiceTest {
// Called in DeviceIdleJobsController constructor.
doReturn(mock(DeviceIdleInternal.class))
.when(() -> LocalServices.getService(DeviceIdleInternal.class));
// Used in JobConcurrencyManager.
doReturn(mock(UserManagerInternal.class))
.when(() -> LocalServices.getService(UserManagerInternal.class));
// Used in JobStatus.
doReturn(mock(PackageManagerInternal.class))
.when(() -> LocalServices.getService(PackageManagerInternal.class));

View File

@@ -16,8 +16,10 @@
package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
@@ -48,6 +50,10 @@ import java.util.Random;
public class WorkCountTrackerTest {
private static final String TAG = "WorkerCountTrackerTest";
private static final double[] EQUAL_PROBABILITY_CDF =
buildCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
1.0 / NUM_WORK_TYPES);
private Random mRandom;
private WorkCountTracker mWorkCountTracker;
@@ -57,6 +63,47 @@ public class WorkCountTrackerTest {
mWorkCountTracker = new WorkCountTracker();
}
@NonNull
private static double[] buildCdf(double pTop, double pEj, double pBg, double pBgUser) {
double[] cdf = new double[JobConcurrencyManager.NUM_WORK_TYPES];
double sum = 0;
sum += pTop;
cdf[0] = sum;
sum += pEj;
cdf[1] = sum;
sum += pBg;
cdf[2] = sum;
sum += pBgUser;
cdf[3] = sum;
if (Double.compare(1, sum) != 0) {
throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
}
return cdf;
}
@JobConcurrencyManager.WorkType
static int getRandomWorkType(double[] cdf, double rand) {
for (int i = cdf.length - 1; i >= 0; --i) {
if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) {
switch (i) {
case 0:
return WORK_TYPE_TOP;
case 1:
return WORK_TYPE_EJ;
case 2:
return WORK_TYPE_BG;
case 3:
return WORK_TYPE_BGUSER;
default:
throw new IllegalStateException("Unknown work type");
}
}
}
throw new IllegalStateException("Couldn't pick random work type");
}
/**
* Represents running and pending jobs.
*/
@@ -64,35 +111,22 @@ public class WorkCountTrackerTest {
public final SparseIntArray running = new SparseIntArray();
public final SparseIntArray pending = new SparseIntArray();
public void maybeEnqueueJobs(double startRatio, double fgJobRatio, double fgUserJobRatio) {
// fgUserJobRatio should always be at least fgJobRatio, otherwise no WORK_TYPE_BG will
// be enqueued.
while (mRandom.nextDouble() < startRatio) {
final double random = mRandom.nextDouble();
if (random < fgJobRatio) {
pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
} else if (random < fgUserJobRatio) {
pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
} else {
pending.put(WORK_TYPE_BGUSER, pending.get(WORK_TYPE_BGUSER) + 1);
}
public void maybeEnqueueJobs(double probStart, double[] typeCdf) {
while (mRandom.nextDouble() < probStart) {
final int workType = getRandomWorkType(typeCdf, mRandom.nextDouble());
pending.put(workType, pending.get(workType) + 1);
}
}
public void maybeFinishJobs(double stopRatio) {
for (int i = running.get(WORK_TYPE_BGUSER); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
running.put(WORK_TYPE_BGUSER, running.get(WORK_TYPE_BGUSER) - 1);
}
}
public void maybeFinishJobs(double probStop) {
for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
if (mRandom.nextDouble() < probStop) {
running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
}
}
for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
if (mRandom.nextDouble() < probStop) {
running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
}
@@ -127,40 +161,27 @@ public class WorkCountTrackerTest {
mWorkCountTracker.onCountDone();
}
private void startPendingJobs(Jobs jobs) {
while ((jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
|| (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)
|| (jobs.pending.get(WORK_TYPE_BGUSER) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BGUSER) != WORK_TYPE_NONE)) {
final boolean isStartingFg = mRandom.nextBoolean();
final boolean isStartingFgUser = mRandom.nextBoolean();
private boolean hasStartablePendingJob(Jobs jobs) {
for (int i = 0; i < jobs.pending.size(); ++i) {
if (jobs.pending.valueAt(i) > 0
&& mWorkCountTracker.canJobStart(jobs.pending.keyAt(i)) != WORK_TYPE_NONE) {
return true;
}
}
return false;
}
if (isStartingFg) {
if (jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_TOP);
mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
}
} else if (isStartingFgUser) {
if (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_BG);
mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
}
} else {
if (jobs.pending.get(WORK_TYPE_BGUSER) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BGUSER) != WORK_TYPE_NONE) {
jobs.pending.put(WORK_TYPE_BGUSER, jobs.pending.get(WORK_TYPE_BGUSER) - 1);
jobs.running.put(WORK_TYPE_BGUSER, jobs.running.get(WORK_TYPE_BGUSER) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_BGUSER);
mWorkCountTracker.onJobStarted(WORK_TYPE_BGUSER);
}
private void startPendingJobs(Jobs jobs) {
while (hasStartablePendingJob(jobs)) {
final int startingWorkType =
getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
if (jobs.pending.get(startingWorkType) > 0
&& mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
jobs.pending.put(startingWorkType, jobs.pending.get(startingWorkType) - 1);
jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
mWorkCountTracker.stageJob(startingWorkType);
mWorkCountTracker.onJobStarted(startingWorkType);
}
}
}
@@ -171,10 +192,10 @@ public class WorkCountTrackerTest {
private void checkRandom(Jobs jobs, int numTests, int totalMax,
@NonNull List<Pair<Integer, Integer>> minLimits,
@NonNull List<Pair<Integer, Integer>> maxLimits,
double startRatio, double fgJobRatio, double fgUserJobRatio, double stopRatio) {
double probStart, double[] typeCdf, double probStop) {
for (int i = 0; i < numTests; i++) {
jobs.maybeFinishJobs(stopRatio);
jobs.maybeEnqueueJobs(startRatio, fgJobRatio, fgUserJobRatio);
jobs.maybeFinishJobs(probStop);
jobs.maybeEnqueueJobs(probStart, typeCdf);
recount(jobs, totalMax, minLimits, maxLimits);
startPendingJobs(jobs);
@@ -200,25 +221,19 @@ public class WorkCountTrackerTest {
*/
@Test
public void testRandom1() {
assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 6;
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double stopRatio = 0.1;
// WorkType probabilities:
// WORK_TYPE_TOP -- 50%
// WORK_TYPE_BG -- 50%
// WORK_TYPE_BGUSER -- 0%
final double fgJobRatio = 0.5;
final double fgUserJobRatio = 1;
final double startRatio = 0.1;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.1;
final double probStart = 0.1;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
EQUAL_PROBABILITY_CDF, probStop);
}
@Test
@@ -230,17 +245,11 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 50%
// WORK_TYPE_BG -- 50%
// WORK_TYPE_BGUSER -- 0%
final double fgJobRatio = 0.5;
final double fgUserJobRatio = 1;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(0.5, 0, 0.5, 0);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -252,17 +261,11 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 33%
// WORK_TYPE_BG -- 33%
// WORK_TYPE_BGUSER -- 33%
final double fgJobRatio = 1 / 3.0;
final double fgUserJobRatio = 2 / 3.0;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -274,17 +277,11 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 33%
// WORK_TYPE_BG -- 33%
// WORK_TYPE_BGUSER -- 33%
final double fgJobRatio = 1 / 3.0;
final double fgUserJobRatio = 2 / 3.0;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -296,17 +293,11 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 10%
// WORK_TYPE_BG -- 80%
// WORK_TYPE_BGUSER -- 10%
final double fgJobRatio = 0.1;
final double fgUserJobRatio = 0.9;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(0.1, 0, 0.8, .1);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -318,17 +309,11 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 90%
// WORK_TYPE_BG -- 10%
// WORK_TYPE_BGUSER -- 0%
final double fgJobRatio = 0.9;
final double fgUserJobRatio = 1;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(0.9, 0, 0.1, 0);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -340,17 +325,11 @@ public class WorkCountTrackerTest {
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.4;
// WorkType probabilities:
// WORK_TYPE_TOP -- 10%
// WORK_TYPE_BG -- 10%
// WORK_TYPE_BGUSER -- 80%
final double fgJobRatio = 0.1;
final double fgUserJobRatio = 0.2;
final double startRatio = 0.5;
final double probStop = 0.4;
final double[] cdf = buildCdf(0.1, 0, 0.1, .8);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -363,17 +342,11 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double stopRatio = 0.4;
// WorkType probabilities:
// WORK_TYPE_TOP -- 90%
// WORK_TYPE_BG -- 5%
// WORK_TYPE_BGUSER -- 5%
final double fgJobRatio = 0.9;
final double fgUserJobRatio = 0.95;
final double startRatio = 0.5;
final double probStop = 0.4;
final double[] cdf = buildCdf(0.9, 0, 0.05, 0.05);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -386,17 +359,11 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 0%
// WORK_TYPE_BG -- 50%
// WORK_TYPE_BGUSER -- 50%
final double fgJobRatio = 0;
final double fgUserJobRatio = 0.5;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(0, 0, 0.5, 0.5);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -409,17 +376,11 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 0%
// WORK_TYPE_BG -- 10%
// WORK_TYPE_BGUSER -- 90%
final double fgJobRatio = 0;
final double fgUserJobRatio = 0.1;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(0, 0, 0.1, 0.9);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -432,17 +393,81 @@ public class WorkCountTrackerTest {
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final double stopRatio = 0.5;
// WorkType probabilities:
// WORK_TYPE_TOP -- 0%
// WORK_TYPE_BG -- 90%
// WORK_TYPE_BGUSER -- 10%
final double fgJobRatio = 0;
final double fgUserJobRatio = 0.9;
final double startRatio = 0.5;
final double probStop = 0.5;
final double[] cdf = buildCdf(0, 0, 0.9, 0.1);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
public void testRandom12() {
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 6;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
final double[] cdf = buildCdf(0.5, 0.5, 0, 0);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
public void testRandom13() {
assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 13;
final List<Pair<Integer, Integer>> maxLimits = List.of(
Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
Pair.create(WORK_TYPE_BGUSER, 3));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
final double probStop = 0.01;
final double probStart = 0.99;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
EQUAL_PROBABILITY_CDF, probStop);
}
@Test
public void testRandom14() {
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 6;
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
final double[] cdf = buildCdf(.1, 0.5, 0.35, 0.05);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
public void testRandom15() {
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 6;
final List<Pair<Integer, Integer>> maxLimits =
List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits =
List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
final double probStop = 0.4;
final double[] cdf = buildCdf(0.01, 0.49, 0.1, 0.4);
final double probStart = 0.5;
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
/** Used by the following tests */
@@ -574,6 +599,16 @@ public class WorkCountTrackerTest {
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_EJ, 5),
Pair.create(WORK_TYPE_BG, 3)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 2)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3),
Pair.create(WORK_TYPE_BG, 3)));
// This could happen if we lower the effective config due to higher memory pressure after
// we've already started running jobs. We shouldn't stop already running jobs, but also
// shouldn't start new ones.

View File

@@ -17,6 +17,7 @@ package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
import static org.junit.Assert.assertEquals;
@@ -36,7 +37,6 @@ import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -44,9 +44,11 @@ import java.util.List;
public class WorkTypeConfigTest {
private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
private static final String KEY_MAX_TOP = "concurrency_max_top_test";
private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
private static final String KEY_MAX_BG = "concurrency_max_bg_test";
private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
private static final String KEY_MIN_TOP = "concurrency_min_top_test";
private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
private static final String KEY_MIN_BG = "concurrency_min_bg_test";
private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
@@ -59,55 +61,27 @@ public class WorkTypeConfigTest {
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
}
private void check(@Nullable DeviceConfig.Properties config,
int defaultTotal,
@Nullable Pair<Integer, Integer> defaultTopLimits,
@Nullable Pair<Integer, Integer> defaultBgLimits,
@Nullable Pair<Integer, Integer> defaultBgUserLimits,
@NonNull List<Pair<Integer, Integer>> defaultMin,
@NonNull List<Pair<Integer, Integer>> defaultMax,
boolean expectedValid, int expectedTotal,
@NonNull Pair<Integer, Integer> expectedTopLimits,
@NonNull Pair<Integer, Integer> expectedBgLimits,
@NonNull Pair<Integer, Integer> expectedBgUserLimits) throws Exception {
@NonNull List<Pair<Integer, Integer>> expectedMinLimits,
@NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception {
resetConfig();
if (config != null) {
DeviceConfig.setProperties(config);
}
List<Pair<Integer, Integer>> defaultMin = new ArrayList<>();
List<Pair<Integer, Integer>> defaultMax = new ArrayList<>();
Integer val;
if (defaultTopLimits != null) {
if ((val = defaultTopLimits.first) != null) {
defaultMin.add(Pair.create(WORK_TYPE_TOP, val));
}
if ((val = defaultTopLimits.second) != null) {
defaultMax.add(Pair.create(WORK_TYPE_TOP, val));
}
}
if (defaultBgLimits != null) {
if ((val = defaultBgLimits.first) != null) {
defaultMin.add(Pair.create(WORK_TYPE_BG, val));
}
if ((val = defaultBgLimits.second) != null) {
defaultMax.add(Pair.create(WORK_TYPE_BG, val));
}
}
if (defaultBgUserLimits != null) {
if ((val = defaultBgUserLimits.first) != null) {
defaultMin.add(Pair.create(WORK_TYPE_BGUSER, val));
}
if ((val = defaultBgUserLimits.second) != null) {
defaultMax.add(Pair.create(WORK_TYPE_BGUSER, val));
}
}
final WorkTypeConfig counts;
try {
counts = new WorkTypeConfig("test",
@@ -128,44 +102,125 @@ public class WorkTypeConfigTest {
counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
assertEquals(expectedTotal, counts.getMaxTotal());
assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
assertEquals((int) expectedBgUserLimits.first, counts.getMinReserved(WORK_TYPE_BGUSER));
assertEquals((int) expectedBgUserLimits.second, counts.getMax(WORK_TYPE_BGUSER));
for (Pair<Integer, Integer> min : expectedMinLimits) {
assertEquals((int) min.second, counts.getMinReserved(min.first));
}
for (Pair<Integer, Integer> max : expectedMaxLimits) {
assertEquals((int) max.second, counts.getMax(max.first));
}
}
@Test
public void test() throws Exception {
// Tests with various combinations.
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1), Pair.create(0, 1),
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1), Pair.create(0, 1));
check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0), Pair.create(0, 0),
/*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1), Pair.create(0, 1));
check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0), Pair.create(0, 0),
/*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1), Pair.create(0, 1));
check(null, /*default*/ -1, null, Pair.create(-1, -1), Pair.create(-1, -1),
/*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1), Pair.create(0, 1));
check(null, /*default*/ 5, null, Pair.create(5, 5), Pair.create(0, 5),
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5), Pair.create(0, 5));
check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5), Pair.create(2, 1),
/*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5), Pair.create(0, 1));
check(null, /*default*/ 4, null, Pair.create(6, 5), Pair.create(6, 5),
/*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4), Pair.create(0, 4));
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1), Pair.create(0, 5),
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1), Pair.create(0, 5));
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1), Pair.create(1, 5),
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1), Pair.create(1, 5));
check(null, /*default*/ 15, null, Pair.create(15, 15), Pair.create(0, 15),
/*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15), Pair.create(0, 15));
check(null, /*default*/ 16, null, Pair.create(16, 16), Pair.create(0, 16),
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16));
check(null, /*default*/ 20, null, Pair.create(20, 20), Pair.create(10, 20),
check(null, /*default*/ 13,
/* min */ List.of(),
/* max */ List.of(),
/*expected*/ true, 13,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 0),
Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 13), Pair.create(WORK_TYPE_EJ, 13),
Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 13)));
check(null, /*default*/ 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
/*expected*/ true, 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
check(null, /*default*/ 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(
Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 1)),
/*expected*/ true, 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
check(null, /*default*/ 0,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 0)),
/*expected*/ false, 1,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
check(null, /*default*/ -1,
/* min */ List.of(Pair.create(WORK_TYPE_BG, -1)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, -1)),
/*expected*/ false, 1,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
check(null, /*default*/ 5,
/* min */ List.of(
Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(
Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
/*expected*/ true, 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)));
check(null, /*default*/ 6,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 2)),
/* max */ List.of(
Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)),
/*expected*/ false, 6,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6),
Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)));
check(null, /*default*/ 4,
/* min */ List.of(
Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 6)),
/* max */ List.of(
Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
/*expected*/ false, 4,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 4),
Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 4)));
check(null, /*default*/ 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
/*expected*/ true, 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
check(null, /*default*/ 10,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
Pair.create(WORK_TYPE_BG, 1)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
/*expected*/ true, 10,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
Pair.create(WORK_TYPE_BG, 1)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)));
check(null, /*default*/ 15,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 15)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 15)),
/*expected*/ true, 15,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 14)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 15), Pair.create(WORK_TYPE_BG, 15)));
check(null, /*default*/ 16,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
/*expected*/ true, 16,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
check(null, /*default*/ 20,
/* min */ List.of(
Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 10)),
/* max */ List.of(
Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 20)),
/*expected*/ false, 16,
Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16));
check(null, /*default*/ 20, null, Pair.create(16, 16), Pair.create(0, 16),
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16));
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
Pair.create(WORK_TYPE_BG, 15), Pair.create(WORK_TYPE_BGUSER, 0)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 16),
Pair.create(WORK_TYPE_BG, 16), Pair.create(WORK_TYPE_BGUSER, 16)));
check(null, /*default*/ 20,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
/*expected*/ true, 16,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
// Test for overriding with a setting string.
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
@@ -173,26 +228,66 @@ public class WorkTypeConfigTest {
.setInt(KEY_MAX_BG, 4)
.setInt(KEY_MIN_BG, 3)
.build(),
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 2),
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4), Pair.create(0, 2));
/*default*/ 9,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/* max */ List.of(
Pair.create(WORK_TYPE_BG, 9), Pair.create(WORK_TYPE_BGUSER, 2)),
/*expected*/ true, 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 5).build(),
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 0),
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5), Pair.create(0, 1));
/*default*/ 9,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/*expected*/ true, 5,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 5)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_BG, 4).build(),
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 9),
/*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4), Pair.create(0, 9));
/*default*/ 9,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/*expected*/ true, 9,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 4)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MIN_BG, 3).build(),
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 6),
/*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9), Pair.create(0, 6));
/*default*/ 9,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/*expected*/ true, 9,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 9)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 20)
.setInt(KEY_MAX_EJ, 5)
.setInt(KEY_MIN_EJ, 2)
.setInt(KEY_MAX_BG, 16)
.setInt(KEY_MIN_BG, 8)
.build(),
/*default*/ 9,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/*expected*/ true, 16,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 2),
Pair.create(WORK_TYPE_BG, 8)),
/* max */
List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_EJ, 5),
Pair.create(WORK_TYPE_BG, 16)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 20)
.setInt(KEY_MAX_BG, 20)
.setInt(KEY_MIN_BG, 8)
.build(),
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 8),
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16), Pair.create(0, 8));
/*default*/ 9,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
/*expected*/ true, 16,
/* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
}
}