You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
774 lines
27 KiB
774 lines
27 KiB
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
|
|
#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
|
|
|
|
#include <utils/Condition.h>
|
|
#include <utils/Mutex.h>
|
|
#include <utils/Timers.h>
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <set>
|
|
#include <map>
|
|
#include <memory>
|
|
|
|
namespace android {
|
|
namespace resource_policy {
|
|
|
|
// Values from frameworks/base/services/core/java/com/android/server/am/ProcessList.java
|
|
const int32_t INVALID_ADJ = -10000;
|
|
const int32_t UNKNOWN_ADJ = 1001;
|
|
const int32_t CACHED_APP_MAX_ADJ = 999;
|
|
const int32_t CACHED_APP_MIN_ADJ = 900;
|
|
const int32_t CACHED_APP_LMK_FIRST_ADJ = 950;
|
|
const int32_t CACHED_APP_IMPORTANCE_LEVELS = 5;
|
|
const int32_t SERVICE_B_ADJ = 800;
|
|
const int32_t PREVIOUS_APP_ADJ = 700;
|
|
const int32_t HOME_APP_ADJ = 600;
|
|
const int32_t SERVICE_ADJ = 500;
|
|
const int32_t HEAVY_WEIGHT_APP_ADJ = 400;
|
|
const int32_t BACKUP_APP_ADJ = 300;
|
|
const int32_t PERCEPTIBLE_LOW_APP_ADJ = 250;
|
|
const int32_t PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
|
|
const int32_t PERCEPTIBLE_APP_ADJ = 200;
|
|
const int32_t VISIBLE_APP_ADJ = 100;
|
|
const int32_t VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
|
|
const int32_t PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
|
|
const int32_t FOREGROUND_APP_ADJ = 0;
|
|
const int32_t PERSISTENT_SERVICE_ADJ = -700;
|
|
const int32_t PERSISTENT_PROC_ADJ = -800;
|
|
const int32_t SYSTEM_ADJ = -900;
|
|
const int32_t NATIVE_ADJ = -1000;
|
|
|
|
class ClientPriority {
|
|
public:
|
|
/**
|
|
* Choosing to set mIsVendorClient through a parameter instead of calling
|
|
* getCurrentServingCall() == BinderCallType::HWBINDER to protect against the
|
|
* case where the construction is offloaded to another thread which isn't a
|
|
* hwbinder thread.
|
|
*/
|
|
ClientPriority(int32_t score, int32_t state, bool isVendorClient, int32_t scoreOffset = 0) :
|
|
mIsVendorClient(isVendorClient), mScoreOffset(scoreOffset) {
|
|
setScore(score);
|
|
setState(state);
|
|
}
|
|
|
|
int32_t getScore() const { return mScore; }
|
|
int32_t getState() const { return mState; }
|
|
int32_t isVendorClient() const { return mIsVendorClient; }
|
|
|
|
void setScore(int32_t score) {
|
|
// For vendor clients, the score is set once and for all during
|
|
// construction. Otherwise, it can get reset each time cameraserver
|
|
// queries ActivityManagerService for oom_adj scores / states .
|
|
// For clients where the score offset is set by the app, add it to the
|
|
// score provided by ActivityManagerService.
|
|
if (score == INVALID_ADJ) {
|
|
mScore = UNKNOWN_ADJ;
|
|
} else {
|
|
mScore = mScoreOffset + score;
|
|
}
|
|
}
|
|
|
|
void setState(int32_t state) {
|
|
// For vendor clients, the score is set once and for all during
|
|
// construction. Otherwise, it can get reset each time cameraserver
|
|
// queries ActivityManagerService for oom_adj scores / states
|
|
// (ActivityManagerService returns a vendor process' state as
|
|
// PROCESS_STATE_NONEXISTENT.
|
|
mState = state;
|
|
}
|
|
|
|
bool operator==(const ClientPriority& rhs) const {
|
|
return (this->mScore == rhs.mScore) && (this->mState == rhs.mState);
|
|
}
|
|
|
|
bool operator< (const ClientPriority& rhs) const {
|
|
if (this->mScore == rhs.mScore) {
|
|
return this->mState < rhs.mState;
|
|
} else {
|
|
return this->mScore < rhs.mScore;
|
|
}
|
|
}
|
|
|
|
bool operator> (const ClientPriority& rhs) const {
|
|
return rhs < *this;
|
|
}
|
|
|
|
bool operator<=(const ClientPriority& rhs) const {
|
|
return !(*this > rhs);
|
|
}
|
|
|
|
bool operator>=(const ClientPriority& rhs) const {
|
|
return !(*this < rhs);
|
|
}
|
|
|
|
private:
|
|
int32_t mScore;
|
|
int32_t mState;
|
|
bool mIsVendorClient = false;
|
|
int32_t mScoreOffset = 0;
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* The ClientDescriptor class is a container for a given key/value pair identifying a shared
|
|
* resource, and the corresponding cost, priority, owner ID, and conflicting keys list used
|
|
* in determining eviction behavior.
|
|
*
|
|
* Aside from the priority, these values are immutable once the ClientDescriptor has been
|
|
* constructed.
|
|
*/
|
|
template<class KEY, class VALUE>
|
|
class ClientDescriptor final {
|
|
public:
|
|
ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
|
|
const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
|
|
bool isVendorClient, int32_t oomScoreOffset);
|
|
ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys,
|
|
int32_t score, int32_t ownerId, int32_t state, bool isVendorClient,
|
|
int32_t oomScoreOffset);
|
|
|
|
~ClientDescriptor();
|
|
|
|
/**
|
|
* Return the key for this descriptor.
|
|
*/
|
|
const KEY& getKey() const;
|
|
|
|
/**
|
|
* Return the value for this descriptor.
|
|
*/
|
|
const VALUE& getValue() const;
|
|
|
|
/**
|
|
* Return the cost for this descriptor.
|
|
*/
|
|
int32_t getCost() const;
|
|
|
|
/**
|
|
* Return the priority for this descriptor.
|
|
*/
|
|
const ClientPriority &getPriority() const;
|
|
|
|
/**
|
|
* Return the owner ID for this descriptor.
|
|
*/
|
|
int32_t getOwnerId() const;
|
|
|
|
/**
|
|
* Return true if the given key is in this descriptor's conflicting keys list.
|
|
*/
|
|
bool isConflicting(const KEY& key) const;
|
|
|
|
/**
|
|
* Return the set of all conflicting keys for this descriptor.
|
|
*/
|
|
std::set<KEY> getConflicting() const;
|
|
|
|
/**
|
|
* Set the proirity for this descriptor.
|
|
*/
|
|
void setPriority(const ClientPriority& priority);
|
|
|
|
// This class is ordered by key
|
|
template<class K, class V>
|
|
friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b);
|
|
|
|
private:
|
|
KEY mKey;
|
|
VALUE mValue;
|
|
int32_t mCost;
|
|
std::set<KEY> mConflicting;
|
|
ClientPriority mPriority;
|
|
int32_t mOwnerId;
|
|
}; // class ClientDescriptor
|
|
|
|
template<class K, class V>
|
|
bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) {
|
|
return a.mKey < b.mKey;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
|
|
const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
|
|
bool isVendorClient, int32_t scoreOffset) :
|
|
mKey{key}, mValue{value}, mCost{cost}, mConflicting{conflictingKeys},
|
|
mPriority(score, state, isVendorClient, scoreOffset),
|
|
mOwnerId{ownerId} {}
|
|
|
|
template<class KEY, class VALUE>
|
|
ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost,
|
|
std::set<KEY>&& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
|
|
bool isVendorClient, int32_t scoreOffset) :
|
|
mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost},
|
|
mConflicting{std::forward<std::set<KEY>>(conflictingKeys)},
|
|
mPriority(score, state, isVendorClient, scoreOffset), mOwnerId{ownerId} {}
|
|
|
|
template<class KEY, class VALUE>
|
|
ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}
|
|
|
|
template<class KEY, class VALUE>
|
|
const KEY& ClientDescriptor<KEY, VALUE>::getKey() const {
|
|
return mKey;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const {
|
|
return mValue;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
int32_t ClientDescriptor<KEY, VALUE>::getCost() const {
|
|
return mCost;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
const ClientPriority& ClientDescriptor<KEY, VALUE>::getPriority() const {
|
|
return mPriority;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const {
|
|
return mOwnerId;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const {
|
|
if (key == mKey) return true;
|
|
for (const auto& x : mConflicting) {
|
|
if (key == x) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const {
|
|
return mConflicting;
|
|
}
|
|
|
|
template<class KEY, class VALUE>
|
|
void ClientDescriptor<KEY, VALUE>::setPriority(const ClientPriority& priority) {
|
|
// We don't use the usual copy constructor here since we want to remember
|
|
// whether a client is a vendor client or not. This could have been wiped
|
|
// off in the incoming priority argument since an AIDL thread might have
|
|
// called getCurrentServingCall() == BinderCallType::HWBINDER after refreshing
|
|
// priorities for old clients through ProcessInfoService::getProcessStatesScoresFromPids().
|
|
if (mPriority.isVendorClient()) {
|
|
return;
|
|
}
|
|
mPriority.setScore(priority.getScore());
|
|
mPriority.setState(priority.getState());
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* A default class implementing the LISTENER interface used by ClientManager.
|
|
*/
|
|
template<class KEY, class VALUE>
|
|
class DefaultEventListener {
|
|
public:
|
|
void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
|
|
void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
|
|
};
|
|
|
|
template<class KEY, class VALUE>
|
|
void DefaultEventListener<KEY, VALUE>::onClientAdded(
|
|
const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}
|
|
|
|
template<class KEY, class VALUE>
|
|
void DefaultEventListener<KEY, VALUE>::onClientRemoved(
|
|
const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* The ClientManager class wraps an LRU-ordered list of active clients and implements eviction
|
|
* behavior for handling shared resource access.
|
|
*
|
|
* When adding a new descriptor, eviction behavior is as follows:
|
|
* - Keys are unique, adding a descriptor with the same key as an existing descriptor will
|
|
* result in the lower-priority of the two being removed. Priority ties result in the
|
|
* LRU descriptor being evicted (this means the incoming descriptor be added in this case).
|
|
* - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list
|
|
* will be removed if they have an equal or lower priority than the incoming descriptor;
|
|
* if any have a higher priority, the incoming descriptor is removed instead.
|
|
* - If the sum of all descriptors' costs, including the incoming descriptor's, is more than
|
|
* the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower
|
|
* priority, and a different owner will be evicted in LRU order until either the cost is less
|
|
* than the max cost, or all descriptors meeting this criteria have been evicted and the
|
|
* incoming descriptor has the highest priority. Otherwise, the incoming descriptor is
|
|
* removed instead.
|
|
*/
|
|
template<class KEY, class VALUE, class LISTENER=DefaultEventListener<KEY, VALUE>>
|
|
class ClientManager {
|
|
public:
|
|
// The default maximum "cost" allowed before evicting
|
|
static constexpr int32_t DEFAULT_MAX_COST = 100;
|
|
|
|
ClientManager();
|
|
explicit ClientManager(int32_t totalCost);
|
|
|
|
/**
|
|
* Add a given ClientDescriptor to the managed list. ClientDescriptors for clients that
|
|
* are evicted by this action are returned in a vector.
|
|
*
|
|
* This may return the ClientDescriptor passed in if it would be evicted.
|
|
*/
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client);
|
|
|
|
/**
|
|
* Given a map containing owner (pid) -> priority mappings, update the priority of each
|
|
* ClientDescriptor with an owner in this mapping.
|
|
*/
|
|
void updatePriorities(const std::map<int32_t,ClientPriority>& ownerPriorityList);
|
|
|
|
/**
|
|
* Remove all ClientDescriptors.
|
|
*/
|
|
void removeAll();
|
|
|
|
/**
|
|
* Remove and return the ClientDescriptor with a given key.
|
|
*/
|
|
std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key);
|
|
|
|
/**
|
|
* Remove the given ClientDescriptor.
|
|
*/
|
|
void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value);
|
|
|
|
/**
|
|
* Return a vector of the ClientDescriptors that would be evicted by adding the given
|
|
* ClientDescriptor.
|
|
*
|
|
* This may return the ClientDescriptor passed in if it would be evicted.
|
|
*/
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
|
|
|
|
/**
|
|
* Return a vector of active ClientDescriptors that prevent this client from being added.
|
|
*/
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
|
|
|
|
/**
|
|
* Return a vector containing all currently active ClientDescriptors.
|
|
*/
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const;
|
|
|
|
/**
|
|
* Return a vector containing all keys of currently active ClientDescriptors.
|
|
*/
|
|
std::vector<KEY> getAllKeys() const;
|
|
|
|
/**
|
|
* Return a vector of the owner tags of all currently active ClientDescriptors (duplicates
|
|
* will be removed).
|
|
*/
|
|
std::vector<int32_t> getAllOwners() const;
|
|
|
|
/**
|
|
* Return the ClientDescriptor corresponding to the given key, or an empty shared pointer
|
|
* if none exists.
|
|
*/
|
|
std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
|
|
|
|
/**
|
|
* Block until the given client is no longer in the active clients list, or the timeout
|
|
* occurred.
|
|
*
|
|
* Returns NO_ERROR if this succeeded, -ETIMEDOUT on a timeout, or a negative error code on
|
|
* failure.
|
|
*/
|
|
status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
|
|
nsecs_t timeout) const;
|
|
|
|
/**
|
|
* Set the current listener for client add/remove events.
|
|
*
|
|
* The listener instance must inherit from the LISTENER class and implement the following
|
|
* methods:
|
|
* void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
|
|
* void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
|
|
*
|
|
* These callback methods will be called with the ClientManager's lock held, and should
|
|
* not call any further ClientManager methods.
|
|
*
|
|
* The onClientRemoved method will be called when the client has been removed or evicted
|
|
* from the ClientManager that this event listener has been added to. The onClientAdded
|
|
* method will be called when the client has been added to the ClientManager that this
|
|
* event listener has been added to.
|
|
*/
|
|
void setListener(const std::shared_ptr<LISTENER>& listener);
|
|
|
|
protected:
|
|
~ClientManager();
|
|
|
|
private:
|
|
|
|
/**
|
|
* Return a vector of the ClientDescriptors that would be evicted by adding the given
|
|
* ClientDescriptor. If returnIncompatibleClients is set to true, instead, return the
|
|
* vector of ClientDescriptors that are higher priority than the incoming client and
|
|
* either conflict with this client, or contribute to the resource cost if that would
|
|
* prevent the incoming client from being added.
|
|
*
|
|
* This may return the ClientDescriptor passed in.
|
|
*/
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
|
|
bool returnIncompatibleClients = false) const;
|
|
|
|
int64_t getCurrentCostLocked() const;
|
|
|
|
mutable Mutex mLock;
|
|
mutable Condition mRemovedCondition;
|
|
int32_t mMaxCost;
|
|
// LRU ordered, most recent at end
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
|
|
std::shared_ptr<LISTENER> mListener;
|
|
}; // class ClientManager
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
ClientManager<KEY, VALUE, LISTENER>::ClientManager() :
|
|
ClientManager(DEFAULT_MAX_COST) {}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
ClientManager<KEY, VALUE, LISTENER>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
ClientManager<KEY, VALUE, LISTENER>::~ClientManager() {}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
|
|
ClientManager<KEY, VALUE, LISTENER>::wouldEvict(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
|
|
Mutex::Autolock lock(mLock);
|
|
return wouldEvictLocked(client);
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
|
|
ClientManager<KEY, VALUE, LISTENER>::getIncompatibleClients(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
|
|
Mutex::Autolock lock(mLock);
|
|
return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
|
|
ClientManager<KEY, VALUE, LISTENER>::wouldEvictLocked(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
|
|
bool returnIncompatibleClients) const {
|
|
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;
|
|
|
|
// Disallow null clients, return input
|
|
if (client == nullptr) {
|
|
evictList.push_back(client);
|
|
return evictList;
|
|
}
|
|
|
|
const KEY& key = client->getKey();
|
|
int32_t cost = client->getCost();
|
|
ClientPriority priority = client->getPriority();
|
|
int32_t owner = client->getOwnerId();
|
|
|
|
int64_t totalCost = getCurrentCostLocked() + cost;
|
|
|
|
// Determine the MRU of the owners tied for having the highest priority
|
|
int32_t highestPriorityOwner = owner;
|
|
ClientPriority highestPriority = priority;
|
|
for (const auto& i : mClients) {
|
|
ClientPriority curPriority = i->getPriority();
|
|
if (curPriority <= highestPriority) {
|
|
highestPriority = curPriority;
|
|
highestPriorityOwner = i->getOwnerId();
|
|
}
|
|
}
|
|
|
|
if (highestPriority == priority) {
|
|
// Switch back owner if the incoming client has the highest priority, as it is MRU
|
|
highestPriorityOwner = owner;
|
|
}
|
|
|
|
// Build eviction list of clients to remove
|
|
for (const auto& i : mClients) {
|
|
const KEY& curKey = i->getKey();
|
|
int32_t curCost = i->getCost();
|
|
ClientPriority curPriority = i->getPriority();
|
|
int32_t curOwner = i->getOwnerId();
|
|
|
|
bool conflicting = (curKey == key || i->isConflicting(key) ||
|
|
client->isConflicting(curKey));
|
|
|
|
if (!returnIncompatibleClients) {
|
|
// Find evicted clients
|
|
|
|
if (conflicting && curPriority < priority) {
|
|
// Pre-existing conflicting client with higher priority exists
|
|
evictList.clear();
|
|
evictList.push_back(client);
|
|
return evictList;
|
|
} else if (conflicting && owner == curOwner) {
|
|
// Pre-existing conflicting client with the same client owner exists
|
|
// Open the same device twice -> most recent open wins
|
|
// Otherwise let the existing client wins to avoid behaviors difference
|
|
// due to how HAL advertising conflicting devices (which is hidden from
|
|
// application)
|
|
if (curKey == key) {
|
|
evictList.push_back(i);
|
|
totalCost -= curCost;
|
|
} else {
|
|
evictList.clear();
|
|
evictList.push_back(client);
|
|
return evictList;
|
|
}
|
|
} else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
|
|
(curPriority >= priority) &&
|
|
!(highestPriorityOwner == owner && owner == curOwner))) {
|
|
// Add a pre-existing client to the eviction list if:
|
|
// - We are adding a client with higher priority that conflicts with this one.
|
|
// - The total cost including the incoming client's is more than the allowable
|
|
// maximum, and the client has a non-zero cost, lower priority, and a different
|
|
// owner than the incoming client when the incoming client has the
|
|
// highest priority.
|
|
evictList.push_back(i);
|
|
totalCost -= curCost;
|
|
}
|
|
} else {
|
|
// Find clients preventing the incoming client from being added
|
|
|
|
if (curPriority < priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {
|
|
// Pre-existing conflicting client with higher priority exists
|
|
evictList.push_back(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Immediately return the incompatible clients if we are calculating these instead
|
|
if (returnIncompatibleClients) {
|
|
return evictList;
|
|
}
|
|
|
|
// If the total cost is too high, return the input unless the input has the highest priority
|
|
if (totalCost > mMaxCost && highestPriorityOwner != owner) {
|
|
evictList.clear();
|
|
evictList.push_back(client);
|
|
return evictList;
|
|
}
|
|
|
|
return evictList;
|
|
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
|
|
ClientManager<KEY, VALUE, LISTENER>::addAndEvict(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
|
|
Mutex::Autolock lock(mLock);
|
|
auto evicted = wouldEvictLocked(client);
|
|
auto it = evicted.begin();
|
|
if (it != evicted.end() && *it == client) {
|
|
return evicted;
|
|
}
|
|
|
|
auto iter = evicted.cbegin();
|
|
|
|
if (iter != evicted.cend()) {
|
|
|
|
if (mListener != nullptr) mListener->onClientRemoved(**iter);
|
|
|
|
// Remove evicted clients from list
|
|
mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
|
|
[&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
|
|
if (curClientPtr->getKey() == (*iter)->getKey()) {
|
|
iter++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}), mClients.end());
|
|
}
|
|
|
|
if (mListener != nullptr) mListener->onClientAdded(*client);
|
|
mClients.push_back(client);
|
|
mRemovedCondition.broadcast();
|
|
|
|
return evicted;
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
|
|
ClientManager<KEY, VALUE, LISTENER>::getAll() const {
|
|
Mutex::Autolock lock(mLock);
|
|
return mClients;
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::vector<KEY> ClientManager<KEY, VALUE, LISTENER>::getAllKeys() const {
|
|
Mutex::Autolock lock(mLock);
|
|
std::vector<KEY> keys(mClients.size());
|
|
for (const auto& i : mClients) {
|
|
keys.push_back(i->getKey());
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::vector<int32_t> ClientManager<KEY, VALUE, LISTENER>::getAllOwners() const {
|
|
Mutex::Autolock lock(mLock);
|
|
std::set<int32_t> owners;
|
|
for (const auto& i : mClients) {
|
|
owners.emplace(i->getOwnerId());
|
|
}
|
|
return std::vector<int32_t>(owners.begin(), owners.end());
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
void ClientManager<KEY, VALUE, LISTENER>::updatePriorities(
|
|
const std::map<int32_t,ClientPriority>& ownerPriorityList) {
|
|
Mutex::Autolock lock(mLock);
|
|
for (auto& i : mClients) {
|
|
auto j = ownerPriorityList.find(i->getOwnerId());
|
|
if (j != ownerPriorityList.end()) {
|
|
i->setPriority(j->second);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::get(
|
|
const KEY& key) const {
|
|
Mutex::Autolock lock(mLock);
|
|
for (const auto& i : mClients) {
|
|
if (i->getKey() == key) return i;
|
|
}
|
|
return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
void ClientManager<KEY, VALUE, LISTENER>::removeAll() {
|
|
Mutex::Autolock lock(mLock);
|
|
if (mListener != nullptr) {
|
|
for (const auto& i : mClients) {
|
|
mListener->onClientRemoved(*i);
|
|
}
|
|
}
|
|
mClients.clear();
|
|
mRemovedCondition.broadcast();
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::remove(
|
|
const KEY& key) {
|
|
Mutex::Autolock lock(mLock);
|
|
|
|
std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;
|
|
|
|
// Remove evicted clients from list
|
|
mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
|
|
[this, &key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
|
|
if (curClientPtr->getKey() == key) {
|
|
if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
|
|
ret = curClientPtr;
|
|
return true;
|
|
}
|
|
return false;
|
|
}), mClients.end());
|
|
|
|
mRemovedCondition.broadcast();
|
|
return ret;
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
status_t ClientManager<KEY, VALUE, LISTENER>::waitUntilRemoved(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
|
|
nsecs_t timeout) const {
|
|
status_t ret = NO_ERROR;
|
|
Mutex::Autolock lock(mLock);
|
|
|
|
bool isRemoved = false;
|
|
|
|
// Figure out what time in the future we should hit the timeout
|
|
nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + timeout;
|
|
|
|
while (!isRemoved) {
|
|
isRemoved = true;
|
|
for (const auto& i : mClients) {
|
|
if (i == client) {
|
|
isRemoved = false;
|
|
}
|
|
}
|
|
|
|
if (!isRemoved) {
|
|
ret = mRemovedCondition.waitRelative(mLock, timeout);
|
|
if (ret != NO_ERROR) {
|
|
break;
|
|
}
|
|
timeout = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
void ClientManager<KEY, VALUE, LISTENER>::setListener(const std::shared_ptr<LISTENER>& listener) {
|
|
Mutex::Autolock lock(mLock);
|
|
mListener = listener;
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
void ClientManager<KEY, VALUE, LISTENER>::remove(
|
|
const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
|
|
Mutex::Autolock lock(mLock);
|
|
// Remove evicted clients from list
|
|
mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
|
|
[this, &value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
|
|
if (curClientPtr == value) {
|
|
if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
|
|
return true;
|
|
}
|
|
return false;
|
|
}), mClients.end());
|
|
mRemovedCondition.broadcast();
|
|
}
|
|
|
|
template<class KEY, class VALUE, class LISTENER>
|
|
int64_t ClientManager<KEY, VALUE, LISTENER>::getCurrentCostLocked() const {
|
|
int64_t totalCost = 0;
|
|
for (const auto& x : mClients) {
|
|
totalCost += x->getCost();
|
|
}
|
|
return totalCost;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
}; // namespace resource_policy
|
|
}; // namespace android
|
|
|
|
#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
|