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