• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.healthconnect;
18 
19 import android.util.Log;
20 
21 import com.android.internal.annotations.GuardedBy;
22 
23 import java.util.Map;
24 import java.util.Queue;
25 import java.util.concurrent.ConcurrentSkipListMap;
26 import java.util.concurrent.LinkedBlockingQueue;
27 
28 /**
29  * A scheduler class to run the tasks in a Round Robin fashion based on client package names.
30  *
31  * @hide
32  */
33 public final class HealthConnectRoundRobinScheduler {
34     private static final String TAG = "HealthConnectScheduler";
35     private final ConcurrentSkipListMap<Integer, Queue<Runnable>> mTasks =
36             new ConcurrentSkipListMap<>();
37     private final Object mLock = new Object();
38 
39     @GuardedBy("mLock")
40     private boolean mPauseScheduler;
41 
42     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
43     @GuardedBy("mLock")
44     private Integer mLastKeyUsed;
45 
resume()46     void resume() {
47         synchronized (mLock) {
48             mPauseScheduler = false;
49         }
50     }
51 
52     @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression
addTask(int uid, Runnable task)53     void addTask(int uid, Runnable task) {
54         synchronized (mLock) {
55             // If the scheduler is currently paused (this can happen if the platform is doing a user
56             // switch), ignore this request. This most likely means that we won't be able to deliver
57             // the result back anyway.
58             if (mPauseScheduler) {
59                 Log.e(TAG, "Unable to schedule task for uid: " + uid);
60                 return;
61             }
62 
63             mTasks.putIfAbsent(uid, new LinkedBlockingQueue<>());
64             mTasks.get(uid).add(task);
65         }
66     }
67 
getNextTask()68     Runnable getNextTask() {
69         synchronized (mLock) {
70             if (mLastKeyUsed == null) {
71                 mLastKeyUsed = mTasks.firstKey();
72             }
73 
74             Map.Entry<Integer, Queue<Runnable>> entry = mTasks.higherEntry(mLastKeyUsed);
75             while (entry != null && entry.getValue().isEmpty()) {
76                 mTasks.remove(entry.getKey());
77                 entry = mTasks.higherEntry(entry.getKey());
78             }
79 
80             if (entry == null) {
81                 // Reached the end, no tasks found. Reset to first entry and try again.
82                 entry = mTasks.firstEntry();
83                 while (entry != null && entry.getValue().isEmpty()) {
84                     mTasks.remove(entry.getKey());
85                     entry = mTasks.higherEntry(entry.getKey());
86                 }
87             }
88 
89             if (entry != null) {
90                 mLastKeyUsed = entry.getKey();
91                 return entry.getValue().poll();
92             }
93 
94             throw new InternalError("Task scheduled but none found");
95         }
96     }
97 
killTasksAndPauseScheduler()98     void killTasksAndPauseScheduler() {
99         synchronized (mLock) {
100             mPauseScheduler = true;
101             mTasks.clear();
102         }
103     }
104 }
105