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
31namespace android {
32namespace resource_policy {
33
34class ClientPriority {
35public:
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
66private:
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 */
81template<class KEY, class VALUE>
82class ClientDescriptor final {
83public:
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
135private:
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
144template<class K, class V>
145bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) {
146    return a.mKey < b.mKey;
147}
148
149template<class KEY, class VALUE>
150ClientDescriptor<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
156template<class KEY, class VALUE>
157ClientDescriptor<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
163template<class KEY, class VALUE>
164ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}
165
166template<class KEY, class VALUE>
167const KEY& ClientDescriptor<KEY, VALUE>::getKey() const {
168    return mKey;
169}
170
171template<class KEY, class VALUE>
172const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const {
173    return mValue;
174}
175
176template<class KEY, class VALUE>
177int32_t ClientDescriptor<KEY, VALUE>::getCost() const {
178    return mCost;
179}
180
181template<class KEY, class VALUE>
182const ClientPriority& ClientDescriptor<KEY, VALUE>::getPriority() const {
183    return mPriority;
184}
185
186template<class KEY, class VALUE>
187int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const {
188    return mOwnerId;
189}
190
191template<class KEY, class VALUE>
192bool 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
200template<class KEY, class VALUE>
201std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const {
202    return mConflicting;
203}
204
205template<class KEY, class VALUE>
206void 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 */
215template<class KEY, class VALUE>
216class DefaultEventListener {
217public:
218    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
219    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
220};
221
222template<class KEY, class VALUE>
223void DefaultEventListener<KEY, VALUE>::onClientAdded(
224        const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}
225
226template<class KEY, class VALUE>
227void 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 */
250template<class KEY, class VALUE, class LISTENER=DefaultEventListener<KEY, VALUE>>
251class ClientManager {
252public:
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
354protected:
355    ~ClientManager();
356
357private:
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
382template<class KEY, class VALUE, class LISTENER>
383ClientManager<KEY, VALUE, LISTENER>::ClientManager() :
384        ClientManager(DEFAULT_MAX_COST) {}
385
386template<class KEY, class VALUE, class LISTENER>
387ClientManager<KEY, VALUE, LISTENER>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
388
389template<class KEY, class VALUE, class LISTENER>
390ClientManager<KEY, VALUE, LISTENER>::~ClientManager() {}
391
392template<class KEY, class VALUE, class LISTENER>
393std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
394ClientManager<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
400template<class KEY, class VALUE, class LISTENER>
401std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
402ClientManager<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
408template<class KEY, class VALUE, class LISTENER>
409std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
410ClientManager<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
501template<class KEY, class VALUE, class LISTENER>
502std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
503ClientManager<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
536template<class KEY, class VALUE, class LISTENER>
537std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
538ClientManager<KEY, VALUE, LISTENER>::getAll() const {
539    Mutex::Autolock lock(mLock);
540    return mClients;
541}
542
543template<class KEY, class VALUE, class LISTENER>
544std::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
553template<class KEY, class VALUE, class LISTENER>
554std::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
563template<class KEY, class VALUE, class LISTENER>
564void 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
575template<class KEY, class VALUE, class LISTENER>
576std::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
585template<class KEY, class VALUE, class LISTENER>
586void 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
597template<class KEY, class VALUE, class LISTENER>
598std::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
619template<class KEY, class VALUE, class LISTENER>
620status_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
651template<class KEY, class VALUE, class LISTENER>
652void ClientManager<KEY, VALUE, LISTENER>::setListener(const std::shared_ptr<LISTENER>& listener) {
653    Mutex::Autolock lock(mLock);
654    mListener = listener;
655}
656
657template<class KEY, class VALUE, class LISTENER>
658void 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
673template<class KEY, class VALUE, class LISTENER>
674int64_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