1 /*
2  * Copyright (C) 2021 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_hardware_automotive_vehicle_utils_include_VehicleObjectPool_H_
18 #define android_hardware_automotive_vehicle_utils_include_VehicleObjectPool_H_
19 
20 #include <deque>
21 #include <map>
22 #include <memory>
23 #include <mutex>
24 
25 #include <VehicleHalTypes.h>
26 
27 #include <android-base/thread_annotations.h>
28 
29 namespace android {
30 namespace hardware {
31 namespace automotive {
32 namespace vehicle {
33 
34 // Handy metric mostly for unit tests and debug.
35 #define INC_METRIC_IF_DEBUG(val) PoolStats::instance()->val++;
36 
37 struct PoolStats {
38     std::atomic<uint32_t> Obtained{0};
39     std::atomic<uint32_t> Created{0};
40     std::atomic<uint32_t> Recycled{0};
41     std::atomic<uint32_t> Deleted{0};
42 
instancePoolStats43     static PoolStats* instance() {
44         static PoolStats inst;
45         return &inst;
46     }
47 };
48 
49 template <typename T>
50 struct Deleter {
51     using OnDeleteFunc = std::function<void(T*)>;
52 
DeleterDeleter53     explicit Deleter(const OnDeleteFunc& f) : mOnDelete(f){};
54 
55     Deleter() = default;
56     Deleter(const Deleter&) = default;
57 
operatorDeleter58     void operator()(T* o) { mOnDelete(o); }
59 
60   private:
61     OnDeleteFunc mOnDelete;
62 };
63 
64 // This is std::unique_ptr<> with custom delete operation that typically moves the pointer it holds
65 // back to ObjectPool.
66 template <typename T>
67 using recyclable_ptr = typename std::unique_ptr<T, Deleter<T>>;
68 
69 // Generic abstract object pool class. Users of this class must implement {@Code createObject}.
70 //
71 // This class is thread-safe. Concurrent calls to {@Code obtain} from multiple threads is OK, also
72 // client can obtain an object in one thread and then move ownership to another thread.
73 template <typename T>
74 class ObjectPool {
75   public:
76     using GetSizeFunc = std::function<size_t(const T&)>;
77 
ObjectPool(size_t maxPoolObjectsSize,GetSizeFunc getSizeFunc)78     ObjectPool(size_t maxPoolObjectsSize, GetSizeFunc getSizeFunc)
79         : mMaxPoolObjectsSize(maxPoolObjectsSize), mGetSizeFunc(getSizeFunc){};
80     virtual ~ObjectPool() = default;
81 
obtain()82     virtual recyclable_ptr<T> obtain() {
83         std::scoped_lock<std::mutex> lock(mLock);
84         INC_METRIC_IF_DEBUG(Obtained)
85         if (mObjects.empty()) {
86             INC_METRIC_IF_DEBUG(Created)
87             return wrap(createObject());
88         }
89 
90         auto o = wrap(mObjects.front().release());
91         mObjects.pop_front();
92         mPoolObjectsSize -= mGetSizeFunc(*o);
93         return o;
94     }
95 
96     ObjectPool& operator=(const ObjectPool&) = delete;
97     ObjectPool(const ObjectPool&) = delete;
98 
99   protected:
100     virtual T* createObject() = 0;
101 
recycle(T * o)102     virtual void recycle(T* o) {
103         std::scoped_lock<std::mutex> lock(mLock);
104         size_t objectSize = mGetSizeFunc(*o);
105 
106         if (objectSize > mMaxPoolObjectsSize ||
107             mPoolObjectsSize > mMaxPoolObjectsSize - objectSize) {
108             INC_METRIC_IF_DEBUG(Deleted)
109 
110             // We have no space left in the pool.
111             delete o;
112             return;
113         }
114 
115         INC_METRIC_IF_DEBUG(Recycled)
116 
117         mObjects.push_back(std::unique_ptr<T>{o});
118         mPoolObjectsSize += objectSize;
119     }
120 
121     const size_t mMaxPoolObjectsSize;
122 
123   private:
getDeleter()124     const Deleter<T>& getDeleter() {
125         if (!mDeleter.get()) {
126             Deleter<T>* d =
127                     new Deleter<T>(std::bind(&ObjectPool::recycle, this, std::placeholders::_1));
128             mDeleter.reset(d);
129         }
130         return *mDeleter.get();
131     }
132 
wrap(T * raw)133     recyclable_ptr<T> wrap(T* raw) { return recyclable_ptr<T>{raw, getDeleter()}; }
134 
135     mutable std::mutex mLock;
136     std::deque<std::unique_ptr<T>> mObjects GUARDED_BY(mLock);
137     std::unique_ptr<Deleter<T>> mDeleter;
138     size_t mPoolObjectsSize GUARDED_BY(mLock);
139     GetSizeFunc mGetSizeFunc;
140 };
141 
142 #undef INC_METRIC_IF_DEBUG
143 
144 // This class provides a pool of recyclable VehiclePropertyValue objects.
145 //
146 // It has only one overloaded public method - obtain(...), users must call this method when new
147 // object is needed with given VehiclePropertyType and vector size (for vector properties). This
148 // method returns a recyclable smart pointer to VehiclePropertyValue, essentially this is a
149 // std::unique_ptr with custom delete function, so recyclable object has only one owner and
150 // developers can safely pass it around. Once this object goes out of scope, it will be returned to
151 // the object pool.
152 //
153 // Some objects are not recyclable: strings and vector data types with vector
154 // length > maxRecyclableVectorSize (provided in the constructor). These objects will be deleted
155 // immediately once the go out of scope. There's no synchronization penalty for these objects since
156 // we do not store them in the pool.
157 //
158 // This class is thread-safe. Users can obtain an object in one thread and pass it to another.
159 //
160 // Sample usage:
161 //
162 //   VehiclePropValuePool pool;
163 //   auto v = pool.obtain(VehiclePropertyType::INT32);
164 //   v->propId = VehicleProperty::HVAC_FAN_SPEED;
165 //   v->areaId = VehicleAreaSeat::ROW_1_LEFT;
166 //   v->timestamp = elapsedRealtimeNano();
167 //   v->value->int32Values[0] = 42;
168 class VehiclePropValuePool {
169   public:
170     using RecyclableType =
171             recyclable_ptr<aidl::android::hardware::automotive::vehicle::VehiclePropValue>;
172 
173     // Creates VehiclePropValuePool
174     //
175     // @param maxRecyclableVectorSize - vector value types (e.g. VehiclePropertyType::INT32_VEC)
176     // with size equal or less to this value will be stored in the pool. If users tries to obtain
177     // value with vector size greater than maxRecyclableVectorSize, user will receive a regular
178     // unique pointer instead of a recyclable pointer. The object would not be recycled once it
179     // goes out of scope, but would be deleted.
180     // @param maxPoolObjectsSize - The approximate upper bound of memory each internal recycling
181     // pool could take. We have 4 different type pools, each with 4 different vector size, so
182     // approximately this pool would at-most take 4 * 4 * 10240 = 160k memory.
183     VehiclePropValuePool(size_t maxRecyclableVectorSize = 4, size_t maxPoolObjectsSize = 10240)
mMaxRecyclableVectorSize(maxRecyclableVectorSize)184         : mMaxRecyclableVectorSize(maxRecyclableVectorSize),
185           mMaxPoolObjectsSize(maxPoolObjectsSize){};
186 
187     // Obtain a recyclable VehiclePropertyValue object from the pool for the given type. If the
188     // given type is not MIXED or STRING, the internal value vector size would be set to 1.
189     // If the given type is MIXED or STRING, all the internal vector sizes would be initialized to
190     // 0.
191     RecyclableType obtain(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type);
192 
193     // Obtain a recyclable VehiclePropertyValue object from the pool for the given type. If the
194     // given type is *_VEC or BYTES, the internal value vector size would be set to vectorSize. If
195     // the given type is BOOLEAN, INT32, FLOAT, or INT64, the internal value vector size would be
196     // set to 1. If the given type is MIXED or STRING, all the internal value vector sizes would be
197     // set to 0. vectorSize must be larger than 0.
198     RecyclableType obtain(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
199                           size_t vectorSize);
200     // Obtain a recyclable VehicePropertyValue object that is a copy of src. If src does not contain
201     // any value or the src property type is not valid, this function would return an empty
202     // VehiclePropValue.
203     RecyclableType obtain(
204             const aidl::android::hardware::automotive::vehicle::VehiclePropValue& src);
205     // Obtain a recyclable boolean object.
206     RecyclableType obtainBoolean(bool value);
207     // Obtain a recyclable int32 object.
208     RecyclableType obtainInt32(int32_t value);
209     // Obtain a recyclable int64 object.
210     RecyclableType obtainInt64(int64_t value);
211     // Obtain a recyclable float object.
212     RecyclableType obtainFloat(float value);
213     // Obtain a recyclable float object.
214     RecyclableType obtainString(const char* cstr);
215     // Obtain a recyclable mixed object.
216     RecyclableType obtainComplex();
217 
218     VehiclePropValuePool(VehiclePropValuePool&) = delete;
219     VehiclePropValuePool& operator=(VehiclePropValuePool&) = delete;
220 
221   private:
isSingleValueType(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type)222     static inline bool isSingleValueType(
223             aidl::android::hardware::automotive::vehicle::VehiclePropertyType type) {
224         return type == aidl::android::hardware::automotive::vehicle::VehiclePropertyType::BOOLEAN ||
225                type == aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32 ||
226                type == aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT64 ||
227                type == aidl::android::hardware::automotive::vehicle::VehiclePropertyType::FLOAT;
228     }
229 
isComplexType(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type)230     static inline bool isComplexType(
231             aidl::android::hardware::automotive::vehicle::VehiclePropertyType type) {
232         return type == aidl::android::hardware::automotive::vehicle::VehiclePropertyType::MIXED ||
233                type == aidl::android::hardware::automotive::vehicle::VehiclePropertyType::STRING;
234     }
235 
isDisposable(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,size_t vectorSize)236     bool isDisposable(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
237                       size_t vectorSize) const {
238         return vectorSize == 0 || vectorSize > mMaxRecyclableVectorSize || isComplexType(type);
239     }
240 
241     RecyclableType obtainDisposable(
242             aidl::android::hardware::automotive::vehicle::VehiclePropertyType valueType,
243             size_t vectorSize) const;
244     RecyclableType obtainRecyclable(
245             aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
246             size_t vectorSize);
247 
248     class InternalPool
249         : public ObjectPool<aidl::android::hardware::automotive::vehicle::VehiclePropValue> {
250       public:
InternalPool(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,size_t vectorSize,size_t maxPoolObjectsSize,ObjectPool::GetSizeFunc getSizeFunc)251         InternalPool(aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
252                      size_t vectorSize, size_t maxPoolObjectsSize,
253                      ObjectPool::GetSizeFunc getSizeFunc)
254             : ObjectPool(maxPoolObjectsSize, getSizeFunc),
255               mPropType(type),
256               mVectorSize(vectorSize) {}
257 
258       protected:
259         aidl::android::hardware::automotive::vehicle::VehiclePropValue* createObject() override;
260         void recycle(aidl::android::hardware::automotive::vehicle::VehiclePropValue* o) override;
261 
262       private:
263         bool check(aidl::android::hardware::automotive::vehicle::RawPropValues* v);
264 
265         template <typename VecType>
check(std::vector<VecType> * vec,bool isVectorType)266         bool check(std::vector<VecType>* vec, bool isVectorType) {
267             return vec->size() == (isVectorType ? mVectorSize : 0);
268         }
269 
270       private:
271         aidl::android::hardware::automotive::vehicle::VehiclePropertyType mPropType;
272         size_t mVectorSize;
273     };
274     const Deleter<aidl::android::hardware::automotive::vehicle::VehiclePropValue>
275             mDisposableDeleter{
276                     [](aidl::android::hardware::automotive::vehicle::VehiclePropValue* v) {
277                         delete v;
278                     }};
279 
280     mutable std::mutex mLock;
281     const size_t mMaxRecyclableVectorSize;
282     const size_t mMaxPoolObjectsSize;
283     // A map with 'property_type' | 'value_vector_size' as key and a recyclable object pool as
284     // value. We would create a recyclable pool for each property type and vector size combination.
285     std::map<int32_t, std::unique_ptr<InternalPool>> mValueTypePools GUARDED_BY(mLock);
286 };
287 
288 }  // namespace vehicle
289 }  // namespace automotive
290 }  // namespace hardware
291 }  // namespace android
292 
293 #endif  // android_hardware_automotive_vehicle_utils_include_VehicleObjectPool_H_
294