Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9de3fb07 authored by Yu Shan's avatar Yu Shan
Browse files

Migrate VehicleObjectPool

Test: atest VehicleHalVehicleUtilsTest
Bug: 199337732
Change-Id: I93772c401dc077f1fbab79e302336494039308d1
parent 36b6d088
Loading
Loading
Loading
Loading
+294 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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_hardware_automotive_vehicle_utils_include_VehicleObjectPool_H_
#define android_hardware_automotive_vehicle_utils_include_VehicleObjectPool_H_

#include <deque>
#include <map>
#include <memory>
#include <mutex>

#include <VehicleHalTypes.h>

#include <android-base/thread_annotations.h>

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {

// Handy metric mostly for unit tests and debug.
#define INC_METRIC_IF_DEBUG(val) PoolStats::instance()->val++;

struct PoolStats {
    std::atomic<uint32_t> Obtained{0};
    std::atomic<uint32_t> Created{0};
    std::atomic<uint32_t> Recycled{0};
    std::atomic<uint32_t> Deleted{0};

    static PoolStats* instance() {
        static PoolStats inst;
        return &inst;
    }
};

template <typename T>
struct Deleter {
    using OnDeleteFunc = std::function<void(T*)>;

    explicit Deleter(const OnDeleteFunc& f) : mOnDelete(f){};

    Deleter() = default;
    Deleter(const Deleter&) = default;

    void operator()(T* o) { mOnDelete(o); }

  private:
    OnDeleteFunc mOnDelete;
};

// This is std::unique_ptr<> with custom delete operation that typically moves the pointer it holds
// back to ObjectPool.
template <typename T>
using recyclable_ptr = typename std::unique_ptr<T, Deleter<T>>;

// Generic abstract object pool class. Users of this class must implement {@Code createObject}.
//
// This class is thread-safe. Concurrent calls to {@Code obtain} from multiple threads is OK, also
// client can obtain an object in one thread and then move ownership to another thread.
template <typename T>
class ObjectPool {
  public:
    using GetSizeFunc = std::function<size_t(const T&)>;

    ObjectPool(size_t maxPoolObjectsSize, GetSizeFunc getSizeFunc)
        : mMaxPoolObjectsSize(maxPoolObjectsSize), mGetSizeFunc(getSizeFunc){};
    virtual ~ObjectPool() = default;

    virtual recyclable_ptr<T> obtain() {
        std::lock_guard<std::mutex> lock(mLock);
        INC_METRIC_IF_DEBUG(Obtained)
        if (mObjects.empty()) {
            INC_METRIC_IF_DEBUG(Created)
            return wrap(createObject());
        }

        auto o = wrap(mObjects.front().release());
        mObjects.pop_front();
        mPoolObjectsSize -= mGetSizeFunc(*o);
        return o;
    }

    ObjectPool& operator=(const ObjectPool&) = delete;
    ObjectPool(const ObjectPool&) = delete;

  protected:
    virtual T* createObject() = 0;

    virtual void recycle(T* o) {
        std::lock_guard<std::mutex> lock(mLock);
        size_t objectSize = mGetSizeFunc(*o);

        if (objectSize > mMaxPoolObjectsSize ||
            mPoolObjectsSize > mMaxPoolObjectsSize - objectSize) {
            INC_METRIC_IF_DEBUG(Deleted)

            // We have no space left in the pool.
            delete o;
            return;
        }

        INC_METRIC_IF_DEBUG(Recycled)

        mObjects.push_back(std::unique_ptr<T>{o});
        mPoolObjectsSize += objectSize;
    }

    const size_t mMaxPoolObjectsSize;

  private:
    const Deleter<T>& getDeleter() {
        if (!mDeleter.get()) {
            Deleter<T>* d =
                    new Deleter<T>(std::bind(&ObjectPool::recycle, this, std::placeholders::_1));
            mDeleter.reset(d);
        }
        return *mDeleter.get();
    }

    recyclable_ptr<T> wrap(T* raw) { return recyclable_ptr<T>{raw, getDeleter()}; }

    mutable std::mutex mLock;
    std::deque<std::unique_ptr<T>> mObjects GUARDED_BY(mLock);
    std::unique_ptr<Deleter<T>> mDeleter;
    size_t mPoolObjectsSize GUARDED_BY(mLock);
    GetSizeFunc mGetSizeFunc;
};

#undef INC_METRIC_IF_DEBUG

// This class provides a pool of recyclable VehiclePropertyValue objects.
//
// It has only one overloaded public method - obtain(...), users must call this method when new
// object is needed with given VehiclePropertyType and vector size (for vector properties). This
// method returns a recyclable smart pointer to VehiclePropertyValue, essentially this is a
// std::unique_ptr with custom delete function, so recyclable object has only one owner and
// developers can safely pass it around. Once this object goes out of scope, it will be returned to
// the object pool.
//
// Some objects are not recyclable: strings and vector data types with vector
// length > maxRecyclableVectorSize (provided in the constructor). These objects will be deleted
// immediately once the go out of scope. There's no synchronization penalty for these objects since
// we do not store them in the pool.
//
// This class is thread-safe. Users can obtain an object in one thread and pass it to another.
//
// Sample usage:
//
//   VehiclePropValuePool pool;
//   auto v = pool.obtain(VehiclePropertyType::INT32);
//   v->propId = VehicleProperty::HVAC_FAN_SPEED;
//   v->areaId = VehicleAreaSeat::ROW_1_LEFT;
//   v->timestamp = elapsedRealtimeNano();
//   v->value->int32Values[0] = 42;
class VehiclePropValuePool {
  public:
    using RecyclableType =
            recyclable_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>;

    // Creates VehiclePropValuePool
    //
    // @param maxRecyclableVectorSize - vector value types (e.g. VehiclePropertyType::INT32_VEC)
    // with size equal or less to this value will be stored in the pool. If users tries to obtain
    // value with vector size greater than maxRecyclableVectorSize, user will receive a regular
    // unique pointer instead of a recyclable pointer. The object would not be recycled once it
    // goes out of scope, but would be deleted.
    // @param maxPoolObjectsSize - The approximate upper bound of memory each internal recycling
    // pool could take. We have 4 different type pools, each with 4 different vector size, so
    // approximately this pool would at-most take 4 * 4 * 10240 = 160k memory.
    VehiclePropValuePool(size_t maxRecyclableVectorSize = 4, size_t maxPoolObjectsSize = 10240)
        : mMaxRecyclableVectorSize(maxRecyclableVectorSize),
          mMaxPoolObjectsSize(maxPoolObjectsSize){};

    // Obtain a recyclable VehiclePropertyValue object from the pool for the given type. If the
    // given type is not MIXED or STRING, the internal value vector size would be set to 1.
    // If the given type is MIXED or STRING, all the internal vector sizes would be initialized to
    // 0.
    RecyclableType obtain(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type);

    // Obtain a recyclable VehiclePropertyValue object from the pool for the given type. If the
    // given type is *_VEC or BYTES, the internal value vector size would be set to vectorSize. If
    // the given type is BOOLEAN, INT32, FLOAT, or INT64, the internal value vector size would be
    // set to 1. If the given type is MIXED or STRING, all the internal value vector sizes would be
    // set to 0. vectorSize must be larger than 0.
    RecyclableType obtain(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
                          size_t vectorSize);
    // Obtain a recyclable VehicePropertyValue object that is a copy of src. If src does not contain
    // any value or the src property type is not valid, this function would return an empty
    // VehiclePropValue.
    RecyclableType obtain(
            const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& src);
    // Obtain a recyclable boolean object.
    RecyclableType obtainBoolean(bool value);
    // Obtain a recyclable int32 object.
    RecyclableType obtainInt32(int32_t value);
    // Obtain a recyclable int64 object.
    RecyclableType obtainInt64(int64_t value);
    // Obtain a recyclable float object.
    RecyclableType obtainFloat(float value);
    // Obtain a recyclable float object.
    RecyclableType obtainString(const char* cstr);
    // Obtain a recyclable mixed object.
    RecyclableType obtainComplex();

    VehiclePropValuePool(VehiclePropValuePool&) = delete;
    VehiclePropValuePool& operator=(VehiclePropValuePool&) = delete;

  private:
    static inline bool isSingleValueType(
            ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type) {
        return type == ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::
                               BOOLEAN ||
               type == ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32 ||
               type == ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT64 ||
               type == ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::FLOAT;
    }

    static inline bool isComplexType(
            ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type) {
        return type == ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::MIXED ||
               type == ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::STRING;
    }

    bool isDisposable(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
                      size_t vectorSize) const {
        return vectorSize > mMaxRecyclableVectorSize || isComplexType(type);
    }

    RecyclableType obtainDisposable(
            ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType valueType,
            size_t vectorSize) const;
    RecyclableType obtainRecyclable(
            ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
            size_t vectorSize);

    class InternalPool
        : public ObjectPool<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> {
      public:
        InternalPool(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
                     size_t vectorSize, size_t maxPoolObjectsSize,
                     ObjectPool::GetSizeFunc getSizeFunc)
            : ObjectPool(maxPoolObjectsSize, getSizeFunc),
              mPropType(type),
              mVectorSize(vectorSize) {}

      protected:
        ::aidl::android::hardware::automotive::vehicle::VehiclePropValue* createObject() override;
        void recycle(::aidl::android::hardware::automotive::vehicle::VehiclePropValue* o) override;

      private:
        bool check(::aidl::android::hardware::automotive::vehicle::RawPropValues* v);

        template <typename VecType>
        bool check(std::vector<VecType>* vec, bool isVectorType) {
            return vec->size() == (isVectorType ? mVectorSize : 0);
        }

      private:
        ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType mPropType;
        size_t mVectorSize;
    };
    const Deleter<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>
            mDisposableDeleter{
                    [](::aidl::android::hardware::automotive::vehicle::VehiclePropValue* v) {
                        delete v;
                    }};

    mutable std::mutex mLock;
    const size_t mMaxRecyclableVectorSize;
    const size_t mMaxPoolObjectsSize;
    // A map with 'property_type' | 'value_vector_size' as key and a recyclable object pool as
    // value. We would create a recyclable pool for each property type and vector size combination.
    std::map<int32_t, std::unique_ptr<InternalPool>> mValueTypePools GUARDED_BY(mLock);
};

}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android

#endif  // android_hardware_automotive_vehicle_utils_include_VehicleObjectPool_H_
+99 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define android_hardware_automotive_vehicle_aidl_impl_utils_common_include_VehicleUtils_H_

#include <VehicleHalTypes.h>
#include <utils/Log.h>

namespace android {
namespace hardware {
@@ -81,6 +82,104 @@ inline const ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig*
    return nullptr;
}

inline std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>
createVehiclePropValueVec(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type,
                          size_t vecSize) {
    auto val = std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>(
            new ::aidl::android::hardware::automotive::vehicle::VehiclePropValue);
    switch (type) {
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32:
            [[fallthrough]];
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::BOOLEAN:
            vecSize = 1;
            [[fallthrough]];
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32_VEC:
            val->value.int32Values.resize(vecSize);
            break;
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::FLOAT:
            vecSize = 1;
            [[fallthrough]];
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::FLOAT_VEC:
            val->value.floatValues.resize(vecSize);
            break;
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT64:
            vecSize = 1;
            [[fallthrough]];
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT64_VEC:
            val->value.int64Values.resize(vecSize);
            break;
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::BYTES:
            val->value.byteValues.resize(vecSize);
            break;
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::STRING:
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::MIXED:
            break;  // Valid, but nothing to do.
        default:
            ALOGE("createVehiclePropValue: unknown type: %d", toInt(type));
            val.reset(nullptr);
    }
    return val;
}

inline std::unique_ptr<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>
createVehiclePropValue(::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type) {
    return createVehiclePropValueVec(type, 1);
}

inline size_t getVehicleRawValueVectorSize(
        const ::aidl::android::hardware::automotive::vehicle::RawPropValues& value,
        ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType type) {
    switch (type) {
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32:  // fall
                                                                                          // through
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::
                BOOLEAN:  // fall through
            return std::min(value.int32Values.size(), static_cast<size_t>(1));
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::FLOAT:
            return std::min(value.floatValues.size(), static_cast<size_t>(1));
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT64:
            return std::min(value.int64Values.size(), static_cast<size_t>(1));
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT32_VEC:
            return value.int32Values.size();
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::FLOAT_VEC:
            return value.floatValues.size();
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::INT64_VEC:
            return value.int64Values.size();
        case ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType::BYTES:
            return value.byteValues.size();
        default:
            ALOGE("getVehicleRawValueVectorSize: unknown type: %d", toInt(type));
            return 0;
    }
}

inline void copyVehicleRawValue(
        ::aidl::android::hardware::automotive::vehicle::RawPropValues* dest,
        const ::aidl::android::hardware::automotive::vehicle::RawPropValues& src) {
    dest->int32Values = src.int32Values;
    dest->floatValues = src.floatValues;
    dest->int64Values = src.int64Values;
    dest->byteValues = src.byteValues;
    dest->stringValue = src.stringValue;
}

// getVehiclePropValueSize returns approximately how much memory 'value' would take. This should
// only be used in a limited-size memory pool to set an upper bound for memory consumption.
inline size_t getVehiclePropValueSize(
        const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& prop) {
    size_t size = 0;
    size += sizeof(prop.timestamp);
    size += sizeof(prop.areaId);
    size += sizeof(prop.prop);
    size += sizeof(prop.status);
    size += prop.value.int32Values.size() * sizeof(int32_t);
    size += prop.value.int64Values.size() * sizeof(int64_t);
    size += prop.value.floatValues.size() * sizeof(float);
    size += prop.value.byteValues.size() * sizeof(uint8_t);
    size += prop.value.stringValue.size();
    return size;
}

}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
+165 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

#define LOG_TAG "VehicleObjectPool"

#include <VehicleObjectPool.h>

#include <VehicleUtils.h>

#include <assert.h>
#include <utils/Log.h>

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {

using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtain(VehiclePropertyType type) {
    if (isComplexType(type)) {
        return obtain(type, 0);
    }
    return obtain(type, 1);
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtain(VehiclePropertyType type,
                                                                  size_t vectorSize) {
    if (isSingleValueType(type)) {
        vectorSize = 1;
    } else if (isComplexType(type)) {
        vectorSize = 0;
    }
    return isDisposable(type, vectorSize) ? obtainDisposable(type, vectorSize)
                                          : obtainRecyclable(type, vectorSize);
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtain(const VehiclePropValue& src) {
    VehiclePropertyType type = getPropType(src.prop);
    size_t vectorSize = getVehicleRawValueVectorSize(src.value, type);
    if (vectorSize == 0 && !isComplexType(type)) {
        ALOGW("empty vehicle prop value, contains no content");
        // Return any empty VehiclePropValue.
        return RecyclableType{new VehiclePropValue, mDisposableDeleter};
    }

    auto dest = obtain(type, vectorSize);

    dest->prop = src.prop;
    dest->areaId = src.areaId;
    dest->status = src.status;
    dest->timestamp = src.timestamp;
    copyVehicleRawValue(&dest->value, src.value);

    return dest;
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainInt32(int32_t value) {
    auto val = obtain(VehiclePropertyType::INT32);
    val->value.int32Values[0] = value;
    return val;
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainInt64(int64_t value) {
    auto val = obtain(VehiclePropertyType::INT64);
    val->value.int64Values[0] = value;
    return val;
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainFloat(float value) {
    auto val = obtain(VehiclePropertyType::FLOAT);
    val->value.floatValues[0] = value;
    return val;
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainString(const char* cstr) {
    auto val = obtain(VehiclePropertyType::STRING);
    val->value.stringValue = cstr;
    return val;
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainComplex() {
    return obtain(VehiclePropertyType::MIXED);
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainRecyclable(
        VehiclePropertyType type, size_t vectorSize) {
    std::lock_guard<std::mutex> lock(mLock);
    assert(vectorSize > 0);

    // VehiclePropertyType is not overlapping with vectorSize.
    int32_t key = static_cast<int32_t>(type) | static_cast<int32_t>(vectorSize);
    auto it = mValueTypePools.find(key);

    if (it == mValueTypePools.end()) {
        auto newPool(std::make_unique<InternalPool>(type, vectorSize, mMaxPoolObjectsSize,
                                                    getVehiclePropValueSize));
        it = mValueTypePools.emplace(key, std::move(newPool)).first;
    }
    return it->second->obtain();
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainBoolean(bool value) {
    return obtainInt32(value);
}

VehiclePropValuePool::RecyclableType VehiclePropValuePool::obtainDisposable(
        VehiclePropertyType valueType, size_t vectorSize) const {
    return RecyclableType{createVehiclePropValueVec(valueType, vectorSize).release(),
                          mDisposableDeleter};
}

void VehiclePropValuePool::InternalPool::recycle(VehiclePropValue* o) {
    if (o == nullptr) {
        ALOGE("Attempt to recycle nullptr");
        return;
    }

    if (!check(&o->value)) {
        ALOGE("Discarding value for prop 0x%x because it contains "
              "data that is not consistent with this pool. "
              "Expected type: %d, vector size: %zu",
              o->prop, toInt(mPropType), mVectorSize);
        delete o;
    } else {
        ObjectPool<VehiclePropValue>::recycle(o);
    }
}

bool VehiclePropValuePool::InternalPool::check(RawPropValues* v) {
    return check(&v->int32Values, (VehiclePropertyType::INT32 == mPropType ||
                                   VehiclePropertyType::INT32_VEC == mPropType ||
                                   VehiclePropertyType::BOOLEAN == mPropType)) &&
           check(&v->floatValues, (VehiclePropertyType::FLOAT == mPropType ||
                                   VehiclePropertyType::FLOAT_VEC == mPropType)) &&
           check(&v->int64Values, (VehiclePropertyType::INT64 == mPropType ||
                                   VehiclePropertyType::INT64_VEC == mPropType)) &&
           check(&v->byteValues, VehiclePropertyType::BYTES == mPropType) &&
           v->stringValue.size() == 0;
}

VehiclePropValue* VehiclePropValuePool::InternalPool::createObject() {
    return createVehiclePropValueVec(mPropType, mVectorSize).release();
}

}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android
+381 −0

File added.

Preview size limit exceeded, changes collapsed.

+125 −0

File changed.

Preview size limit exceeded, changes collapsed.