Merge "Apply thermal throttling to long-running jobs."

This commit is contained in:
Kweku Adams
2022-07-22 01:34:51 +00:00
committed by Android (Google) Code Review
6 changed files with 121 additions and 17 deletions

View File

@@ -542,6 +542,30 @@ class JobConcurrencyManager {
return mRunningJobs.contains(job);
}
/**
* Return {@code true} if the specified job has been executing for longer than the minimum
* execution guarantee.
*/
@GuardedBy("mLock")
boolean isJobLongRunningLocked(@NonNull JobStatus job) {
if (!mRunningJobs.contains(job)) {
return false;
}
for (int i = mActiveServices.size() - 1; i >= 0; --i) {
final JobServiceContext jsc = mActiveServices.get(i);
final JobStatus jobStatus = jsc.getRunningJobLocked();
if (jobStatus == job) {
return !jsc.isWithinExecutionGuaranteeTime();
}
}
Slog.wtf(TAG, "Couldn't find long running job on a context");
mRunningJobs.remove(job);
return false;
}
/**
* Returns true if a job that is "similar" to the provided job is currently running.
* "Similar" in this context means any job that the {@link JobStore} would consider equivalent
@@ -989,6 +1013,26 @@ class JobConcurrencyManager {
}
}
/**
* Stops any jobs that have run for more than their minimum execution guarantee and are
* restricted by the given {@link JobRestriction}.
*/
@GuardedBy("mLock")
void maybeStopLongRunningJobsLocked(@NonNull JobRestriction restriction) {
for (int i = mActiveServices.size() - 1; i >= 0; --i) {
final JobServiceContext jsc = mActiveServices.get(i);
final JobStatus jobStatus = jsc.getRunningJobLocked();
if (jobStatus != null && !jsc.isWithinExecutionGuaranteeTime()
&& restriction.isJobRestricted(jobStatus)) {
jsc.cancelExecutingJobLocked(restriction.getReason(),
restriction.getInternalReason(),
JobParameters.getInternalReasonCodeDescription(
restriction.getInternalReason()));
}
}
}
@GuardedBy("mLock")
void stopNonReadyActiveJobsLocked() {
for (int i = 0; i < mActiveServices.size(); i++) {
@@ -1349,6 +1393,12 @@ class JobConcurrencyManager {
if (mPowerManager.isDeviceIdleMode()) {
return "deep doze";
}
final JobRestriction jobRestriction;
if ((jobRestriction = mService.checkIfRestricted(js)) != null) {
return "restriction:"
+ JobParameters.getInternalReasonCodeDescription(
jobRestriction.getInternalReason());
}
// Update config in case memory usage has changed significantly.
updateCounterConfigLocked();

View File

@@ -1772,6 +1772,12 @@ public class JobSchedulerService extends com.android.server.SystemService
return mConcurrencyManager.isJobRunningLocked(job);
}
/** @see JobConcurrencyManager#isJobLongRunningLocked(JobStatus) */
@GuardedBy("mLock")
public boolean isLongRunningLocked(JobStatus job) {
return mConcurrencyManager.isJobLongRunningLocked(job);
}
private void noteJobPending(JobStatus job) {
mJobPackageTracker.notePending(job);
}
@@ -2049,6 +2055,17 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
@Override
public void onRestrictionStateChanged(@NonNull JobRestriction restriction,
boolean stopLongRunningJobs) {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
if (stopLongRunningJobs) {
synchronized (mLock) {
mConcurrencyManager.maybeStopLongRunningJobsLocked(restriction);
}
}
}
@Override
public void onRunJobNow(JobStatus jobStatus) {
if (jobStatus == null) {

View File

@@ -736,10 +736,10 @@ public final class JobServiceContext implements ServiceConnection {
@GuardedBy("mLock")
private void doCancelLocked(@JobParameters.StopReason int stopReasonCode,
int internalStopReasonCode, @Nullable String debugReason) {
if (mVerb == VERB_FINISHED) {
if (mVerb == VERB_FINISHED || mVerb == VERB_STOPPING) {
if (DEBUG) {
Slog.d(TAG,
"Trying to process cancel for torn-down context, ignoring.");
"Too late to process cancel for context (verb=" + mVerb + "), ignoring.");
}
return;
}

View File

@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.util.ArraySet;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.restrictions.JobRestriction;
import java.util.List;
@@ -36,6 +37,16 @@ public interface StateChangedListener {
*/
void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs);
/**
* Called by a {@link com.android.server.job.restrictions.JobRestriction} to notify the
* JobScheduler that it should check on the state of all jobs.
*
* @param stopLongRunningJobs Whether to stop any jobs that have run for more than their minimum
* execution guarantee and are restricted by the changed restriction
*/
void onRestrictionStateChanged(@NonNull JobRestriction restriction,
boolean stopLongRunningJobs);
/**
* Called by the controller to notify the JobManager that regardless of the state of the task,
* it must be run immediately.

View File

@@ -73,9 +73,10 @@ public class ThermalStatusRestriction extends JobRestriction {
// bucket (thus resulting in us beginning to enforce the tightest
// restrictions).
|| (mThermalStatus < UPPER_THRESHOLD && status > UPPER_THRESHOLD);
final boolean increased = mThermalStatus < status;
mThermalStatus = status;
if (significantChange) {
mService.onControllerStateChanged(null);
mService.onRestrictionStateChanged(ThermalStatusRestriction.this, increased);
}
}
});
@@ -89,16 +90,19 @@ public class ThermalStatusRestriction extends JobRestriction {
final int priority = job.getEffectivePriority();
if (mThermalStatus >= HIGHER_PRIORITY_THRESHOLD) {
// For moderate throttling, only let expedited jobs and high priority regular jobs that
// are already running run.
// haven't been running for long run.
return !job.shouldTreatAsExpeditedJob()
&& !(priority == JobInfo.PRIORITY_HIGH
&& mService.isCurrentlyRunningLocked(job));
&& mService.isCurrentlyRunningLocked(job)
&& !mService.isLongRunningLocked(job));
}
if (mThermalStatus >= LOW_PRIORITY_THRESHOLD) {
// For light throttling, throttle all min priority jobs and all low priority jobs that
// aren't already running.
return (priority == JobInfo.PRIORITY_LOW && !mService.isCurrentlyRunningLocked(job))
|| priority == JobInfo.PRIORITY_MIN;
// aren't already running or have been running for long enough.
return priority == JobInfo.PRIORITY_MIN
|| (priority == JobInfo.PRIORITY_LOW
&& (!mService.isCurrentlyRunningLocked(job)
|| mService.isLongRunningLocked(job)));
}
return false;
}

View File

@@ -33,6 +33,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -111,42 +113,46 @@ public class ThermalStatusRestrictionTest {
InOrder inOrder = inOrder(mJobSchedulerService);
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_NONE);
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService, never())
.onRestrictionStateChanged(any(), anyBoolean());
assertEquals(THERMAL_STATUS_NONE, mThermalStatusRestriction.getThermalStatus());
// Moving within LOW and UPPER thresholds
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_LIGHT);
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
assertEquals(THERMAL_STATUS_LIGHT, mThermalStatusRestriction.getThermalStatus());
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_MODERATE);
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
assertEquals(THERMAL_STATUS_MODERATE, mThermalStatusRestriction.getThermalStatus());
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SEVERE);
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
assertEquals(THERMAL_STATUS_SEVERE, mThermalStatusRestriction.getThermalStatus());
// Changing outside of range
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_CRITICAL);
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService, never())
.onRestrictionStateChanged(any(), eq(true));
assertEquals(THERMAL_STATUS_CRITICAL, mThermalStatusRestriction.getThermalStatus());
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_EMERGENCY);
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService, never())
.onRestrictionStateChanged(any(), anyBoolean());
assertEquals(THERMAL_STATUS_EMERGENCY, mThermalStatusRestriction.getThermalStatus());
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SHUTDOWN);
inOrder.verify(mJobSchedulerService, never()).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService, never())
.onRestrictionStateChanged(any(), anyBoolean());
assertEquals(THERMAL_STATUS_SHUTDOWN, mThermalStatusRestriction.getThermalStatus());
// Cross values we care about
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_NONE);
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(false));
assertEquals(THERMAL_STATUS_NONE, mThermalStatusRestriction.getThermalStatus());
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_EMERGENCY);
inOrder.verify(mJobSchedulerService).onControllerStateChanged(any());
inOrder.verify(mJobSchedulerService).onRestrictionStateChanged(any(), eq(true));
assertEquals(THERMAL_STATUS_EMERGENCY, mThermalStatusRestriction.getThermalStatus());
}
@@ -160,12 +166,16 @@ public class ThermalStatusRestrictionTest {
createJobBuilder(2).setPriority(JobInfo.PRIORITY_LOW).build());
final JobStatus jobLowPriorityRunning = createJobStatus("testIsJobRestricted",
createJobBuilder(3).setPriority(JobInfo.PRIORITY_LOW).build());
final JobStatus jobLowPriorityRunningLong = createJobStatus("testIsJobRestricted",
createJobBuilder(9).setPriority(JobInfo.PRIORITY_LOW).build());
final JobStatus jobDefaultPriority = createJobStatus("testIsJobRestricted",
createJobBuilder(4).setPriority(JobInfo.PRIORITY_DEFAULT).build());
final JobStatus jobHighPriority = createJobStatus("testIsJobRestricted",
createJobBuilder(5).setPriority(JobInfo.PRIORITY_HIGH).build());
final JobStatus jobHighPriorityRunning = createJobStatus("testIsJobRestricted",
createJobBuilder(6).setPriority(JobInfo.PRIORITY_HIGH).build());
final JobStatus jobHighPriorityRunningLong = createJobStatus("testIsJobRestricted",
createJobBuilder(10).setPriority(JobInfo.PRIORITY_HIGH).build());
final JobStatus ejDowngraded = createJobStatus("testIsJobRestricted",
createJobBuilder(7).setExpedited(true).build());
final JobStatus ej = spy(createJobStatus("testIsJobRestricted",
@@ -174,13 +184,17 @@ public class ThermalStatusRestrictionTest {
when(mJobSchedulerService.isCurrentlyRunningLocked(jobLowPriorityRunning)).thenReturn(true);
when(mJobSchedulerService.isCurrentlyRunningLocked(jobHighPriorityRunning))
.thenReturn(true);
when(mJobSchedulerService.isLongRunningLocked(jobLowPriorityRunningLong)).thenReturn(true);
when(mJobSchedulerService.isLongRunningLocked(jobHighPriorityRunningLong)).thenReturn(true);
assertFalse(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
assertFalse(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
@@ -189,9 +203,11 @@ public class ThermalStatusRestrictionTest {
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
assertFalse(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
@@ -200,9 +216,11 @@ public class ThermalStatusRestrictionTest {
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
assertFalse(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
assertFalse(mThermalStatusRestriction.isJobRestricted(ej));
@@ -211,9 +229,11 @@ public class ThermalStatusRestrictionTest {
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
assertTrue(mThermalStatusRestriction.isJobRestricted(ej));
@@ -222,9 +242,11 @@ public class ThermalStatusRestrictionTest {
assertTrue(mThermalStatusRestriction.isJobRestricted(jobMinPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobLowPriorityRunningLong));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobDefaultPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriority));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(jobHighPriorityRunningLong));
assertTrue(mThermalStatusRestriction.isJobRestricted(ejDowngraded));
assertTrue(mThermalStatusRestriction.isJobRestricted(ej));
}