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
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 * A default class implementing the LISTENER interface used by ClientManager.
176 */
177template<class KEY, class VALUE>
178class DefaultEventListener {
179public:
180    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
181    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
182};
183
184template<class KEY, class VALUE>
185void DefaultEventListener<KEY, VALUE>::onClientAdded(
186        const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}
187
188template<class KEY, class VALUE>
189void DefaultEventListener<KEY, VALUE>::onClientRemoved(
190        const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}
191
192// --------------------------------------------------------------------------------
193
194/**
195 * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction
196 * behavior for handling shared resource access.
197 *
198 * When adding a new descriptor, eviction behavior is as follows:
199 *   - Keys are unique, adding a descriptor with the same key as an existing descriptor will
200 *     result in the lower-priority of the two being removed.  Priority ties result in the
201 *     LRU descriptor being evicted (this means the incoming descriptor be added in this case).
202 *   - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list
203 *     will be removed if they have an equal or lower priority than the incoming descriptor;
204 *     if any have a higher priority, the incoming descriptor is removed instead.
205 *   - If the sum of all descriptors' costs, including the incoming descriptor's, is more than
206 *     the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower
207 *     priority, and a different owner will be evicted in LRU order until either the cost is less
208 *     than the max cost, or all descriptors meeting this criteria have been evicted and the
209 *     incoming descriptor has the highest priority.  Otherwise, the incoming descriptor is
210 *     removed instead.
211 */
212template<class KEY, class VALUE, class LISTENER=DefaultEventListener<KEY, VALUE>>
213class ClientManager {
214public:
215    // The default maximum "cost" allowed before evicting
216    static constexpr int32_t DEFAULT_MAX_COST = 100;
217
218    ClientManager();
219    ClientManager(int32_t totalCost);
220
221    /**
222     * Add a given ClientDescriptor to the managed list.  ClientDescriptors for clients that
223     * are evicted by this action are returned in a vector.
224     *
225     * This may return the ClientDescriptor passed in if it would be evicted.
226     */
227    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict(
228            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client);
229
230    /**
231     * Given a map containing owner (pid) -> priority mappings, update the priority of each
232     * ClientDescriptor with an owner in this mapping.
233     */
234    void updatePriorities(const std::map<int32_t,int32_t>& ownerPriorityList);
235
236    /**
237     * Remove all ClientDescriptors.
238     */
239    void removeAll();
240
241    /**
242     * Remove and return the ClientDescriptor with a given key.
243     */
244    std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key);
245
246    /**
247     * Remove the given ClientDescriptor.
248     */
249    void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value);
250
251    /**
252     * Return a vector of the ClientDescriptors that would be evicted by adding the given
253     * ClientDescriptor.
254     *
255     * This may return the ClientDescriptor passed in if it would be evicted.
256     */
257    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict(
258            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
259
260    /**
261     * Return a vector of active ClientDescriptors that prevent this client from being added.
262     */
263    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients(
264            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
265
266    /**
267     * Return a vector containing all currently active ClientDescriptors.
268     */
269    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const;
270
271    /**
272     * Return a vector containing all keys of currently active ClientDescriptors.
273     */
274    std::vector<KEY> getAllKeys() const;
275
276    /**
277     * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates
278     * will be removed).
279     */
280    std::vector<int32_t> getAllOwners() const;
281
282    /**
283     * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer
284     * if none exists.
285     */
286    std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
287
288    /**
289     * Block until the given client is no longer in the active clients list, or the timeout
290     * occurred.
291     *
292     * Returns NO_ERROR if this succeeded, -ETIMEDOUT on a timeout, or a negative error code on
293     * failure.
294     */
295    status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
296            nsecs_t timeout) const;
297
298    /**
299     * Set the current listener for client add/remove events.
300     *
301     * The listener instance must inherit from the LISTENER class and implement the following
302     * methods:
303     *    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
304     *    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
305     *
306     * These callback methods will be called with the ClientManager's lock held, and should
307     * not call any further ClientManager methods.
308     *
309     * The onClientRemoved method will be called when the client has been removed or evicted
310     * from the ClientManager that this event listener has been added to. The onClientAdded
311     * method will be called when the client has been added to the ClientManager that this
312     * event listener has been added to.
313     */
314    void setListener(const std::shared_ptr<LISTENER>& listener);
315
316protected:
317    ~ClientManager();
318
319private:
320
321    /**
322     * Return a vector of the ClientDescriptors that would be evicted by adding the given
323     * ClientDescriptor.  If returnIncompatibleClients is set to true, instead, return the
324     * vector of ClientDescriptors that are higher priority than the incoming client and
325     * either conflict with this client, or contribute to the resource cost if that would
326     * prevent the incoming client from being added.
327     *
328     * This may return the ClientDescriptor passed in.
329     */
330    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked(
331            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
332            bool returnIncompatibleClients = false) const;
333
334    int64_t getCurrentCostLocked() const;
335
336    mutable Mutex mLock;
337    mutable Condition mRemovedCondition;
338    int32_t mMaxCost;
339    // LRU ordered, most recent at end
340    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
341    std::shared_ptr<LISTENER> mListener;
342}; // class ClientManager
343
344template<class KEY, class VALUE, class LISTENER>
345ClientManager<KEY, VALUE, LISTENER>::ClientManager() :
346        ClientManager(DEFAULT_MAX_COST) {}
347
348template<class KEY, class VALUE, class LISTENER>
349ClientManager<KEY, VALUE, LISTENER>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
350
351template<class KEY, class VALUE, class LISTENER>
352ClientManager<KEY, VALUE, LISTENER>::~ClientManager() {}
353
354template<class KEY, class VALUE, class LISTENER>
355std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
356ClientManager<KEY, VALUE, LISTENER>::wouldEvict(
357        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
358    Mutex::Autolock lock(mLock);
359    return wouldEvictLocked(client);
360}
361
362template<class KEY, class VALUE, class LISTENER>
363std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
364ClientManager<KEY, VALUE, LISTENER>::getIncompatibleClients(
365        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
366    Mutex::Autolock lock(mLock);
367    return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
368}
369
370template<class KEY, class VALUE, class LISTENER>
371std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
372ClientManager<KEY, VALUE, LISTENER>::wouldEvictLocked(
373        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
374        bool returnIncompatibleClients) const {
375
376    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;
377
378    // Disallow null clients, return input
379    if (client == nullptr) {
380        evictList.push_back(client);
381        return evictList;
382    }
383
384    const KEY& key = client->getKey();
385    int32_t cost = client->getCost();
386    int32_t priority = client->getPriority();
387    int32_t owner = client->getOwnerId();
388
389    int64_t totalCost = getCurrentCostLocked() + cost;
390
391    // Determine the MRU of the owners tied for having the highest priority
392    int32_t highestPriorityOwner = owner;
393    int32_t highestPriority = priority;
394    for (const auto& i : mClients) {
395        int32_t curPriority = i->getPriority();
396        if (curPriority >= highestPriority) {
397            highestPriority = curPriority;
398            highestPriorityOwner = i->getOwnerId();
399        }
400    }
401
402    if (highestPriority == priority) {
403        // Switch back owner if the incoming client has the highest priority, as it is MRU
404        highestPriorityOwner = owner;
405    }
406
407    // Build eviction list of clients to remove
408    for (const auto& i : mClients) {
409        const KEY& curKey = i->getKey();
410        int32_t curCost = i->getCost();
411        int32_t curPriority = i->getPriority();
412        int32_t curOwner = i->getOwnerId();
413
414        bool conflicting = (curKey == key || i->isConflicting(key) ||
415                client->isConflicting(curKey));
416
417        if (!returnIncompatibleClients) {
418            // Find evicted clients
419
420            if (conflicting && curPriority > priority) {
421                // Pre-existing conflicting client with higher priority exists
422                evictList.clear();
423                evictList.push_back(client);
424                return evictList;
425            } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
426                    (curPriority <= priority) &&
427                    !(highestPriorityOwner == owner && owner == curOwner))) {
428                // Add a pre-existing client to the eviction list if:
429                // - We are adding a client with higher priority that conflicts with this one.
430                // - The total cost including the incoming client's is more than the allowable
431                //   maximum, and the client has a non-zero cost, lower priority, and a different
432                //   owner than the incoming client when the incoming client has the
433                //   highest priority.
434                evictList.push_back(i);
435                totalCost -= curCost;
436            }
437        } else {
438            // Find clients preventing the incoming client from being added
439
440            if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {
441                // Pre-existing conflicting client with higher priority exists
442                evictList.push_back(i);
443            }
444        }
445    }
446
447    // Immediately return the incompatible clients if we are calculating these instead
448    if (returnIncompatibleClients) {
449        return evictList;
450    }
451
452    // If the total cost is too high, return the input unless the input has the highest priority
453    if (totalCost > mMaxCost && highestPriorityOwner != owner) {
454        evictList.clear();
455        evictList.push_back(client);
456        return evictList;
457    }
458
459    return evictList;
460
461}
462
463template<class KEY, class VALUE, class LISTENER>
464std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
465ClientManager<KEY, VALUE, LISTENER>::addAndEvict(
466        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
467    Mutex::Autolock lock(mLock);
468    auto evicted = wouldEvictLocked(client);
469    auto it = evicted.begin();
470    if (it != evicted.end() && *it == client) {
471        return evicted;
472    }
473
474    auto iter = evicted.cbegin();
475
476    if (iter != evicted.cend()) {
477
478        if (mListener != nullptr) mListener->onClientRemoved(**iter);
479
480        // Remove evicted clients from list
481        mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
482            [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
483                if (curClientPtr->getKey() == (*iter)->getKey()) {
484                    iter++;
485                    return true;
486                }
487                return false;
488            }), mClients.end());
489    }
490
491    if (mListener != nullptr) mListener->onClientAdded(*client);
492    mClients.push_back(client);
493    mRemovedCondition.broadcast();
494
495    return evicted;
496}
497
498template<class KEY, class VALUE, class LISTENER>
499std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
500ClientManager<KEY, VALUE, LISTENER>::getAll() const {
501    Mutex::Autolock lock(mLock);
502    return mClients;
503}
504
505template<class KEY, class VALUE, class LISTENER>
506std::vector<KEY> ClientManager<KEY, VALUE, LISTENER>::getAllKeys() const {
507    Mutex::Autolock lock(mLock);
508    std::vector<KEY> keys(mClients.size());
509    for (const auto& i : mClients) {
510        keys.push_back(i->getKey());
511    }
512    return keys;
513}
514
515template<class KEY, class VALUE, class LISTENER>
516std::vector<int32_t> ClientManager<KEY, VALUE, LISTENER>::getAllOwners() const {
517    Mutex::Autolock lock(mLock);
518    std::set<int32_t> owners;
519    for (const auto& i : mClients) {
520        owners.emplace(i->getOwnerId());
521    }
522    return std::vector<int32_t>(owners.begin(), owners.end());
523}
524
525template<class KEY, class VALUE, class LISTENER>
526void ClientManager<KEY, VALUE, LISTENER>::updatePriorities(
527        const std::map<int32_t,int32_t>& ownerPriorityList) {
528    Mutex::Autolock lock(mLock);
529    for (auto& i : mClients) {
530        auto j = ownerPriorityList.find(i->getOwnerId());
531        if (j != ownerPriorityList.end()) {
532            i->setPriority(j->second);
533        }
534    }
535}
536
537template<class KEY, class VALUE, class LISTENER>
538std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::get(
539        const KEY& key) const {
540    Mutex::Autolock lock(mLock);
541    for (const auto& i : mClients) {
542        if (i->getKey() == key) return i;
543    }
544    return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
545}
546
547template<class KEY, class VALUE, class LISTENER>
548void ClientManager<KEY, VALUE, LISTENER>::removeAll() {
549    Mutex::Autolock lock(mLock);
550    if (mListener != nullptr) {
551        for (const auto& i : mClients) {
552            mListener->onClientRemoved(*i);
553        }
554    }
555    mClients.clear();
556    mRemovedCondition.broadcast();
557}
558
559template<class KEY, class VALUE, class LISTENER>
560std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::remove(
561    const KEY& key) {
562    Mutex::Autolock lock(mLock);
563
564    std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;
565
566    // Remove evicted clients from list
567    mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
568        [this, &key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
569            if (curClientPtr->getKey() == key) {
570                if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
571                ret = curClientPtr;
572                return true;
573            }
574            return false;
575        }), mClients.end());
576
577    mRemovedCondition.broadcast();
578    return ret;
579}
580
581template<class KEY, class VALUE, class LISTENER>
582status_t ClientManager<KEY, VALUE, LISTENER>::waitUntilRemoved(
583        const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
584        nsecs_t timeout) const {
585    status_t ret = NO_ERROR;
586    Mutex::Autolock lock(mLock);
587
588    bool isRemoved = false;
589
590    // Figure out what time in the future we should hit the timeout
591    nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + timeout;
592
593    while (!isRemoved) {
594        isRemoved = true;
595        for (const auto& i : mClients) {
596            if (i == client) {
597                isRemoved = false;
598            }
599        }
600
601        if (!isRemoved) {
602            ret = mRemovedCondition.waitRelative(mLock, timeout);
603            if (ret != NO_ERROR) {
604                break;
605            }
606            timeout = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
607        }
608    }
609
610    return ret;
611}
612
613template<class KEY, class VALUE, class LISTENER>
614void ClientManager<KEY, VALUE, LISTENER>::setListener(const std::shared_ptr<LISTENER>& listener) {
615    Mutex::Autolock lock(mLock);
616    mListener = listener;
617}
618
619template<class KEY, class VALUE, class LISTENER>
620void ClientManager<KEY, VALUE, LISTENER>::remove(
621        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
622    Mutex::Autolock lock(mLock);
623    // Remove evicted clients from list
624    mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
625        [this, &value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
626            if (curClientPtr == value) {
627                if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
628                return true;
629            }
630            return false;
631        }), mClients.end());
632    mRemovedCondition.broadcast();
633}
634
635template<class KEY, class VALUE, class LISTENER>
636int64_t ClientManager<KEY, VALUE, LISTENER>::getCurrentCostLocked() const {
637    int64_t totalCost = 0;
638    for (const auto& x : mClients) {
639            totalCost += x->getCost();
640    }
641    return totalCost;
642}
643
644// --------------------------------------------------------------------------------
645
646}; // namespace resource_policy
647}; // namespace android
648
649#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
650