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

Commit 53716467 authored by Yu Shan's avatar Yu Shan Committed by Android (Google) Code Review
Browse files

Merge "Migrate VehicleObjectPool"

parents dcc4f039 9de3fb07
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.