blob: ad5486d5f95bfe33a94844605b62a21c6da7a7d2 [file] [log] [blame]
Ruben Brunkcc776712015-02-17 20:18:47 -08001/*
2 * Copyright (C) 2015 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#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
18#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
19
20#include <utils/Mutex.h>
21
22#include <algorithm>
23#include <utility>
24#include <vector>
25#include <set>
26#include <map>
27#include <memory>
28
29namespace android {
30namespace resource_policy {
31
32// --------------------------------------------------------------------------------
33
34/**
35 * The ClientDescriptor class is a container for a given key/value pair identifying a shared
36 * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used
37 * in determining eviction behavior.
38 *
39 * Aside from the priority, these values are immutable once the ClientDescriptor has been
40 * constructed.
41 */
42template<class KEY, class VALUE>
43class ClientDescriptor final {
44public:
45 ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
46 const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId);
47 ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys,
48 int32_t priority, int32_t ownerId);
49
50 ~ClientDescriptor();
51
52 /**
53 * Return the key for this descriptor.
54 */
55 const KEY& getKey() const;
56
57 /**
58 * Return the value for this descriptor.
59 */
60 const VALUE& getValue() const;
61
62 /**
63 * Return the cost for this descriptor.
64 */
65 int32_t getCost() const;
66
67 /**
68 * Return the priority for this descriptor.
69 */
70 int32_t getPriority() const;
71
72 /**
73 * Return the owner ID for this descriptor.
74 */
75 int32_t getOwnerId() const;
76
77 /**
78 * Return true if the given key is in this descriptor's conflicting keys list.
79 */
80 bool isConflicting(const KEY& key) const;
81
82 /**
83 * Return the set of all conflicting keys for this descriptor.
84 */
85 std::set<KEY> getConflicting() const;
86
87 /**
88 * Set the proirity for this descriptor.
89 */
90 void setPriority(int32_t priority);
91
92 // This class is ordered by key
93 template<class K, class V>
94 friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b);
95
96private:
97 KEY mKey;
98 VALUE mValue;
99 int32_t mCost;
100 std::set<KEY> mConflicting;
101 int32_t mPriority;
102 int32_t mOwnerId;
103}; // class ClientDescriptor
104
105template<class K, class V>
106bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) {
107 return a.mKey < b.mKey;
108}
109
110template<class KEY, class VALUE>
111ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
112 const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key},
113 mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority},
114 mOwnerId{ownerId} {}
115
116template<class KEY, class VALUE>
117ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost,
118 std::set<KEY>&& conflictingKeys, int32_t priority, int32_t ownerId) :
119 mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost},
120 mConflicting{std::forward<std::set<KEY>>(conflictingKeys)}, mPriority{priority},
121 mOwnerId{ownerId} {}
122
123template<class KEY, class VALUE>
124ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}
125
126template<class KEY, class VALUE>
127const KEY& ClientDescriptor<KEY, VALUE>::getKey() const {
128 return mKey;
129}
130
131template<class KEY, class VALUE>
132const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const {
133 return mValue;
134}
135
136template<class KEY, class VALUE>
137int32_t ClientDescriptor<KEY, VALUE>::getCost() const {
138 return mCost;
139}
140
141template<class KEY, class VALUE>
142int32_t ClientDescriptor<KEY, VALUE>::getPriority() const {
143 return mPriority;
144}
145
146template<class KEY, class VALUE>
147int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const {
148 return mOwnerId;
149}
150
151template<class KEY, class VALUE>
152bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const {
153 if (key == mKey) return true;
154 for (const auto& x : mConflicting) {
155 if (key == x) return true;
156 }
157 return false;
158}
159
160template<class KEY, class VALUE>
161std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const {
162 return mConflicting;
163}
164
165template<class KEY, class VALUE>
166void ClientDescriptor<KEY, VALUE>::setPriority(int32_t priority) {
167 mPriority = priority;
168}
169
170// --------------------------------------------------------------------------------
171
172/**
173 * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction
174 * behavior for handling shared resource access.
175 *
176 * When adding a new descriptor, eviction behavior is as follows:
177 * - Keys are unique, adding a descriptor with the same key as an existing descriptor will
178 * result in the lower-priority of the two being removed. Priority ties result in the
179 * LRU descriptor being evicted (this means the incoming descriptor be added in this case).
180 * - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list
181 * will be removed if they have an equal or lower priority than the incoming descriptor;
182 * if any have a higher priority, the incoming descriptor is removed instead.
183 * - If the sum of all descriptors' costs, including the incoming descriptor's, is more than
184 * the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower
185 * priority, and a different owner will be evicted in LRU order until either the cost is less
186 * than the max cost, or all descriptors meeting this criteria have been evicted and the
187 * incoming descriptor has the highest priority. Otherwise, the incoming descriptor is
188 * removed instead.
189 */
190template<class KEY, class VALUE>
191class ClientManager {
192public:
193 // The default maximum "cost" allowed before evicting
194 static constexpr int32_t DEFAULT_MAX_COST = 100;
195
196 ClientManager();
197 ClientManager(int32_t totalCost);
198
199 /**
200 * Add a given ClientDescriptor to the managed list. ClientDescriptors for clients that
201 * are evicted by this action are returned in a vector.
202 *
203 * This may return the ClientDescriptor passed in if it would be evicted.
204 */
205 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict(
206 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client);
207
208 /**
209 * Given a map containing owner (pid) -> priority mappings, update the priority of each
210 * ClientDescriptor with an owner in this mapping.
211 */
212 void updatePriorities(const std::map<int32_t,int32_t>& ownerPriorityList);
213
214 /**
215 * Remove all ClientDescriptors.
216 */
217 void removeAll();
218
219 /**
220 * Remove and return the ClientDescriptor with a given key.
221 */
222 std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key);
223
224 /**
225 * Remove the given ClientDescriptor.
226 */
227 void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value);
228
229 /**
230 * Return a vector of the ClientDescriptors that would be evicted by adding the given
231 * ClientDescriptor.
232 *
233 * This may return the ClientDescriptor passed in if it would be evicted.
234 */
235 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict(
236 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
237
238 /**
239 * Return a vector of active ClientDescriptors that prevent this client from being added.
240 */
241 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients(
242 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
243
244 /**
245 * Return a vector containing all currently active ClientDescriptors.
246 */
247 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const;
248
249 /**
250 * Return a vector containing all keys of currently active ClientDescriptors.
251 */
252 std::vector<KEY> getAllKeys() const;
253
254 /**
255 * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates
256 * will be removed).
257 */
258 std::vector<int32_t> getAllOwners() const;
259
260 /**
261 * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer
262 * if none exists.
263 */
264 std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
265
266protected:
267 ~ClientManager();
268
269private:
270
271 /**
272 * Return a vector of the ClientDescriptors that would be evicted by adding the given
273 * ClientDescriptor. If returnIncompatibleClients is set to true, instead, return the
274 * vector of ClientDescriptors that are higher priority than the incoming client and
275 * either conflict with this client, or contribute to the resource cost if that would
276 * prevent the incoming client from being added.
277 *
278 * This may return the ClientDescriptor passed in.
279 */
280 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked(
281 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
282 bool returnIncompatibleClients = false) const;
283
284 int64_t getCurrentCostLocked() const;
285
286 mutable Mutex mLock;
287 int32_t mMaxCost;
288 // LRU ordered, most recent at end
289 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
290}; // class ClientManager
291
292template<class KEY, class VALUE>
293ClientManager<KEY, VALUE>::ClientManager() :
294 ClientManager(DEFAULT_MAX_COST) {}
295
296template<class KEY, class VALUE>
297ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
298
299template<class KEY, class VALUE>
300ClientManager<KEY, VALUE>::~ClientManager() {}
301
302template<class KEY, class VALUE>
303std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict(
304 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
305 Mutex::Autolock lock(mLock);
306 return wouldEvictLocked(client);
307}
308
309template<class KEY, class VALUE>
310std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
311ClientManager<KEY, VALUE>::getIncompatibleClients(
312 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
313 Mutex::Autolock lock(mLock);
314 return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
315}
316
317template<class KEY, class VALUE>
318std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
319ClientManager<KEY, VALUE>::wouldEvictLocked(
320 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
321 bool returnIncompatibleClients) const {
322
323 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;
324
325 // Disallow null clients, return input
326 if (client == nullptr) {
327 evictList.push_back(client);
328 return evictList;
329 }
330
331 const KEY& key = client->getKey();
332 int32_t cost = client->getCost();
333 int32_t priority = client->getPriority();
334 int32_t owner = client->getOwnerId();
335
336 int64_t totalCost = getCurrentCostLocked() + cost;
337
338 // Determine the MRU of the owners tied for having the highest priority
339 int32_t highestPriorityOwner = owner;
340 int32_t highestPriority = priority;
341 for (const auto& i : mClients) {
342 int32_t curPriority = i->getPriority();
343 if (curPriority >= highestPriority) {
344 highestPriority = curPriority;
345 highestPriorityOwner = i->getOwnerId();
346 }
347 }
348
349 if (highestPriority == priority) {
350 // Switch back owner if the incoming client has the highest priority, as it is MRU
351 highestPriorityOwner = owner;
352 }
353
354 // Build eviction list of clients to remove
355 for (const auto& i : mClients) {
356 const KEY& curKey = i->getKey();
357 int32_t curCost = i->getCost();
358 int32_t curPriority = i->getPriority();
359 int32_t curOwner = i->getOwnerId();
360
361 bool conflicting = (curKey == key || i->isConflicting(key) ||
362 client->isConflicting(curKey));
363
364 if (!returnIncompatibleClients) {
365 // Find evicted clients
366
367 if (conflicting && curPriority > priority) {
368 // Pre-existing conflicting client with higher priority exists
369 evictList.clear();
370 evictList.push_back(client);
371 return evictList;
372 } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
373 (curPriority <= priority) &&
374 !(highestPriorityOwner == owner && owner == curOwner))) {
375 // Add a pre-existing client to the eviction list if:
376 // - We are adding a client with higher priority that conflicts with this one.
377 // - The total cost including the incoming client's is more than the allowable
378 // maximum, and the client has a non-zero cost, lower priority, and a different
379 // owner than the incoming client when the incoming client has the
380 // highest priority.
381 evictList.push_back(i);
382 totalCost -= curCost;
383 }
384 } else {
385 // Find clients preventing the incoming client from being added
386
387 if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {
388 // Pre-existing conflicting client with higher priority exists
389 evictList.push_back(i);
390 }
391 }
392 }
393
394 // Immediately return the incompatible clients if we are calculating these instead
395 if (returnIncompatibleClients) {
396 return evictList;
397 }
398
399 // If the total cost is too high, return the input unless the input has the highest priority
400 if (totalCost > mMaxCost && highestPriorityOwner != owner) {
401 evictList.clear();
402 evictList.push_back(client);
403 return evictList;
404 }
405
406 return evictList;
407
408}
409
410template<class KEY, class VALUE>
411std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict(
412 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
413 Mutex::Autolock lock(mLock);
414 auto evicted = wouldEvictLocked(client);
415 auto it = evicted.begin();
416 if (it != evicted.end() && *it == client) {
417 return evicted;
418 }
419
420 auto iter = evicted.cbegin();
421
422 // Remove evicted clients from list
423 mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
424 [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
425 if (curClientPtr->getKey() == (*iter)->getKey()) {
426 iter++;
427 return true;
428 }
429 return false;
430 }), mClients.end());
431
432 mClients.push_back(client);
433
434 return evicted;
435}
436
437template<class KEY, class VALUE>
438std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
439ClientManager<KEY, VALUE>::getAll() const {
440 Mutex::Autolock lock(mLock);
441 return mClients;
442}
443
444template<class KEY, class VALUE>
445std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const {
446 Mutex::Autolock lock(mLock);
447 std::vector<KEY> keys(mClients.size());
448 for (const auto& i : mClients) {
449 keys.push_back(i->getKey());
450 }
451 return keys;
452}
453
454template<class KEY, class VALUE>
455std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const {
456 Mutex::Autolock lock(mLock);
457 std::set<int32_t> owners;
458 for (const auto& i : mClients) {
459 owners.emplace(i->getOwnerId());
460 }
461 return std::vector<int32_t>(owners.begin(), owners.end());
462}
463
464template<class KEY, class VALUE>
465void ClientManager<KEY, VALUE>::updatePriorities(
466 const std::map<int32_t,int32_t>& ownerPriorityList) {
467 Mutex::Autolock lock(mLock);
468 for (auto& i : mClients) {
469 auto j = ownerPriorityList.find(i->getOwnerId());
470 if (j != ownerPriorityList.end()) {
471 i->setPriority(j->second);
472 }
473 }
474}
475
476template<class KEY, class VALUE>
477std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get(
478 const KEY& key) const {
479 Mutex::Autolock lock(mLock);
480 for (const auto& i : mClients) {
481 if (i->getKey() == key) return i;
482 }
483 return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
484}
485
486template<class KEY, class VALUE>
487void ClientManager<KEY, VALUE>::removeAll() {
488 Mutex::Autolock lock(mLock);
489 mClients.clear();
490}
491
492template<class KEY, class VALUE>
493std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) {
494 Mutex::Autolock lock(mLock);
495
496 std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;
497
498 // Remove evicted clients from list
499 mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
500 [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
501 if (curClientPtr->getKey() == key) {
502 ret = curClientPtr;
503 return true;
504 }
505 return false;
506 }), mClients.end());
507
508 return ret;
509}
510
511template<class KEY, class VALUE>
512void ClientManager<KEY, VALUE>::remove(
513 const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
514 Mutex::Autolock lock(mLock);
515 // Remove evicted clients from list
516 mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
517 [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
518 if (curClientPtr == value) {
519 return true;
520 }
521 return false;
522 }), mClients.end());
523}
524
525template<class KEY, class VALUE>
526int64_t ClientManager<KEY, VALUE>::getCurrentCostLocked() const {
527 int64_t totalCost = 0;
528 for (const auto& x : mClients) {
529 totalCost += x->getCost();
530 }
531 return totalCost;
532}
533
534// --------------------------------------------------------------------------------
535
536}; // namespace resource_policy
537}; // namespace android
538
539#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H