Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ba3a072 authored by Kweku Adams's avatar Kweku Adams
Browse files

Sort ContextAssignments to stop longer running jobs first.

Bug: 141645789
Test: atest
frameworks/base/services/tests/servicestests/src/com/android/server/job
frameworks/base/services/tests/mockingservicestests/src/com/android/server/job

Change-Id: Ia067935fdd204a7c6f9227522626e28d474c1e92
parent d9a4f1cd
Loading
Loading
Loading
Loading
+53 −14
Original line number Original line Diff line number Diff line
@@ -68,6 +68,8 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Predicate;
@@ -267,10 +269,44 @@ class JobConcurrencyManager {
                    )
                    )
            );
            );


    /**
     * Comparator to sort the determination lists, putting the ContextAssignments that we most
     * prefer to use at the end of the list.
     */
    private static final Comparator<ContextAssignment> sDeterminationComparator = (ca1, ca2) -> {
        if (ca1 == ca2) {
            return 0;
        }
        final JobStatus js1 = ca1.context.getRunningJobLocked();
        final JobStatus js2 = ca2.context.getRunningJobLocked();
        // Prefer using an empty context over one with a running job.
        if (js1 == null) {
            if (js2 == null) {
                return 0;
            }
            return 1;
        } else if (js2 == null) {
            return -1;
        }
        // We would prefer to replace bg jobs over TOP jobs.
        if (js1.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
            if (js2.lastEvaluatedBias != JobInfo.BIAS_TOP_APP) {
                return -1;
            }
        } else if (js2.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) {
            return 1;
        }
        // Prefer replacing the job that has been running the longest.
        return Long.compare(
                ca2.context.getExecutionStartTimeElapsed(),
                ca1.context.getExecutionStartTimeElapsed());
    };

    // We reuse the lists to avoid GC churn.
    private final ArraySet<ContextAssignment> mRecycledChanged = new ArraySet<>();
    private final ArraySet<ContextAssignment> mRecycledChanged = new ArraySet<>();
    private final ArraySet<ContextAssignment> mRecycledIdle = new ArraySet<>();
    private final ArraySet<ContextAssignment> mRecycledIdle = new ArraySet<>();
    private final ArraySet<ContextAssignment> mRecycledPreferredUidOnly = new ArraySet<>();
    private final ArrayList<ContextAssignment> mRecycledPreferredUidOnly = new ArrayList<>();
    private final ArraySet<ContextAssignment> mRecycledStoppable = new ArraySet<>();
    private final ArrayList<ContextAssignment> mRecycledStoppable = new ArrayList<>();


    private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
    private final Pools.Pool<ContextAssignment> mContextAssignmentPool =
            new Pools.SimplePool<>(MAX_JOB_CONTEXTS_COUNT);
            new Pools.SimplePool<>(MAX_JOB_CONTEXTS_COUNT);
@@ -576,8 +612,8 @@ class JobConcurrencyManager {
        // To avoid GC churn, we recycle the arrays.
        // To avoid GC churn, we recycle the arrays.
        final ArraySet<ContextAssignment> changed = mRecycledChanged;
        final ArraySet<ContextAssignment> changed = mRecycledChanged;
        final ArraySet<ContextAssignment> idle = mRecycledIdle;
        final ArraySet<ContextAssignment> idle = mRecycledIdle;
        final ArraySet<ContextAssignment> preferredUidOnly = mRecycledPreferredUidOnly;
        final ArrayList<ContextAssignment> preferredUidOnly = mRecycledPreferredUidOnly;
        final ArraySet<ContextAssignment> stoppable = mRecycledStoppable;
        final ArrayList<ContextAssignment> stoppable = mRecycledStoppable;


        updateCounterConfigLocked();
        updateCounterConfigLocked();
        // Reset everything since we'll re-evaluate the current state.
        // Reset everything since we'll re-evaluate the current state.
@@ -612,6 +648,8 @@ class JobConcurrencyManager {
                preferredUidOnly.add(assignment);
                preferredUidOnly.add(assignment);
            }
            }
        }
        }
        preferredUidOnly.sort(sDeterminationComparator);
        stoppable.sort(sDeterminationComparator);
        for (int i = numRunningJobs; i < MAX_JOB_CONTEXTS_COUNT; ++i) {
        for (int i = numRunningJobs; i < MAX_JOB_CONTEXTS_COUNT; ++i) {
            final JobServiceContext jsc;
            final JobServiceContext jsc;
            final int numIdleContexts = mIdleContexts.size();
            final int numIdleContexts = mIdleContexts.size();
@@ -670,8 +708,8 @@ class JobConcurrencyManager {
            }
            }
            if (selectedContext == null) {
            if (selectedContext == null) {
                for (int s = stoppable.size() - 1; s >= 0; --s) {
                for (int s = stoppable.size() - 1; s >= 0; --s) {
                    ContextAssignment assignment = stoppable.valueAt(s);
                    final ContextAssignment assignment = stoppable.get(s);
                    JobStatus runningJob = assignment.context.getRunningJobLocked();
                    final JobStatus runningJob = assignment.context.getRunningJobLocked();
                    // Maybe stop the job if it has had its day in the sun. Don't let a different
                    // Maybe stop the job if it has had its day in the sun. Don't let a different
                    // app preempt jobs started for TOP apps though.
                    // app preempt jobs started for TOP apps though.
                    if (runningJob.lastEvaluatedBias < JobInfo.BIAS_TOP_APP
                    if (runningJob.lastEvaluatedBias < JobInfo.BIAS_TOP_APP
@@ -685,7 +723,7 @@ class JobConcurrencyManager {
                            assignment.preemptReason = assignment.shouldStopJobReason;
                            assignment.preemptReason = assignment.shouldStopJobReason;
                            assignment.preemptReasonCode = JobParameters.STOP_REASON_DEVICE_STATE;
                            assignment.preemptReasonCode = JobParameters.STOP_REASON_DEVICE_STATE;
                            selectedContext = assignment;
                            selectedContext = assignment;
                            stoppable.removeAt(s);
                            stoppable.remove(s);
                            assignment.newJob = nextPending;
                            assignment.newJob = nextPending;
                            assignment.newWorkType = replaceWorkType;
                            assignment.newWorkType = replaceWorkType;
                            break;
                            break;
@@ -696,7 +734,7 @@ class JobConcurrencyManager {
            if (selectedContext == null) {
            if (selectedContext == null) {
                int lowestBiasSeen = Integer.MAX_VALUE;
                int lowestBiasSeen = Integer.MAX_VALUE;
                for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
                for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
                    final ContextAssignment assignment = preferredUidOnly.valueAt(p);
                    final ContextAssignment assignment = preferredUidOnly.get(p);
                    final JobStatus runningJob = assignment.context.getRunningJobLocked();
                    final JobStatus runningJob = assignment.context.getRunningJobLocked();
                    if (runningJob.getUid() != nextPending.getUid()) {
                    if (runningJob.getUid() != nextPending.getUid()) {
                        continue;
                        continue;
@@ -767,7 +805,7 @@ class JobConcurrencyManager {
            mContextAssignmentPool.release(assignment);
            mContextAssignmentPool.release(assignment);
        }
        }
        for (int s = stoppable.size() - 1; s >= 0; --s) {
        for (int s = stoppable.size() - 1; s >= 0; --s) {
            final ContextAssignment assignment = stoppable.valueAt(s);
            final ContextAssignment assignment = stoppable.get(s);
            // The preferred UID is set when we cancel with PREEMPT reason, but don't preserve the
            // The preferred UID is set when we cancel with PREEMPT reason, but don't preserve the
            // UID for any stoppable contexts since we want to open the context up to any/all apps.
            // UID for any stoppable contexts since we want to open the context up to any/all apps.
            assignment.context.clearPreferredUid();
            assignment.context.clearPreferredUid();
@@ -775,7 +813,7 @@ class JobConcurrencyManager {
            mContextAssignmentPool.release(assignment);
            mContextAssignmentPool.release(assignment);
        }
        }
        for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
        for (int p = preferredUidOnly.size() - 1; p >= 0; --p) {
            final ContextAssignment assignment = preferredUidOnly.valueAt(p);
            final ContextAssignment assignment = preferredUidOnly.get(p);
            assignment.clear();
            assignment.clear();
            mContextAssignmentPool.release(assignment);
            mContextAssignmentPool.release(assignment);
        }
        }
@@ -1275,12 +1313,12 @@ class JobConcurrencyManager {
        return s.toString();
        return s.toString();
    }
    }


    private static String printAssignments(String header, ArraySet<ContextAssignment>... list) {
    private static String printAssignments(String header, Collection<ContextAssignment>... list) {
        final StringBuilder s = new StringBuilder(header + ": ");
        final StringBuilder s = new StringBuilder(header + ": ");
        for (int l = 0; l < list.length; ++l) {
        for (int l = 0; l < list.length; ++l) {
            ArraySet<ContextAssignment> assignments = list[l];
            final Collection<ContextAssignment> assignments = list[l];
            for (int c = 0; c < assignments.size(); ++c) {
            int c = 0;
                final ContextAssignment assignment = assignments.valueAt(c);
            for (final ContextAssignment assignment : assignments) {
                final JobStatus job = assignment.newJob == null
                final JobStatus job = assignment.newJob == null
                        ? assignment.context.getRunningJobLocked() : assignment.newJob;
                        ? assignment.context.getRunningJobLocked() : assignment.newJob;


@@ -1294,6 +1332,7 @@ class JobConcurrencyManager {
                    s.append(job.getJobId()).append("/").append(job.getUid());
                    s.append(job.getJobId()).append("/").append(job.getUid());
                }
                }
                s.append(")");
                s.append(")");
                c++;
            }
            }
        }
        }
        return s.toString();
        return s.toString();