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