/*
* Copyright (C) 2016 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.
*/
#include <thread>
#include <gtest/gtest.h>
#include <utils/SystemClock.h>
#include "vhal_v2_0/VehicleObjectPool.h"
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
namespace {
class VehicleObjectPoolTest : public ::testing::Test {
protected:
void SetUp() override {
stats = PoolStats::instance();
resetStats();
valuePool.reset(new VehiclePropValuePool);
}
void TearDown() override {
// At the end, all created objects should be either recycled or deleted.
// Some objects could be recycled multiple times, that's why it's <=
ASSERT_EQ(stats->Obtained, stats->Recycled);
ASSERT_LE(stats->Created, stats->Recycled);
}
private:
void resetStats() {
stats->Obtained = 0;
stats->Created = 0;
stats->Recycled = 0;
}
public:
PoolStats* stats;
std::unique_ptr<VehiclePropValuePool> valuePool;
};
TEST_F(VehicleObjectPoolTest, valuePoolBasicCorrectness) {
void* raw = valuePool->obtain(VehiclePropertyType::INT32).get();
// At this point, v1 should be recycled and the only object in the pool.
ASSERT_EQ(raw, valuePool->obtain(VehiclePropertyType::INT32).get());
// Obtaining value of another type - should return a new object
ASSERT_NE(raw, valuePool->obtain(VehiclePropertyType::FLOAT).get());
ASSERT_EQ(3u, stats->Obtained);
ASSERT_EQ(2u, stats->Created);
}
TEST_F(VehicleObjectPoolTest, valuePoolStrings) {
valuePool->obtain(VehiclePropertyType::STRING);
auto vs = valuePool->obtain(VehiclePropertyType::STRING);
vs->value.stringValue = "Hello";
void* raw = vs.get();
vs.reset(); // delete the pointer
auto vs2 = valuePool->obtain(VehiclePropertyType::STRING);
ASSERT_EQ(0u, vs2->value.stringValue.size());
ASSERT_NE(raw, valuePool->obtain(VehiclePropertyType::STRING).get());
ASSERT_EQ(0u, stats->Obtained);
}
TEST_F(VehicleObjectPoolTest, valuePoolMultithreadedBenchmark) {
// In this test we have T threads that concurrently in C cycles
// obtain and release O VehiclePropValue objects of FLOAT / INT32 types.
const int T = 2;
const int C = 500;
const int O = 100;
auto poolPtr = valuePool.get();
std::vector<std::thread> threads;
auto start = elapsedRealtimeNano();
for (int i = 0; i < T; i++) {
threads.push_back(std::thread([&poolPtr] () {
for (int j = 0; j < C; j++) {
std::vector<recyclable_ptr<VehiclePropValue>> vec;
for (int k = 0; k < O; k++) {
vec.push_back(
poolPtr->obtain(k % 2 == 0
? VehiclePropertyType::FLOAT
: VehiclePropertyType::INT32));
}
}
}));
}
for (auto& t : threads) {
t.join();
}
auto finish = elapsedRealtimeNano();
ASSERT_EQ(static_cast<uint32_t>(T * C * O), stats->Obtained);
ASSERT_EQ(static_cast<uint32_t>(T * C * O), stats->Recycled);
// Created less than obtained.
ASSERT_GE(static_cast<uint32_t>(T * O), stats->Created);
auto elapsedMs = (finish - start) / 1000000;
ASSERT_GE(1000, elapsedMs); // Less a second to access 100K objects.
// Typically it takes about 0.1s on Nexus6P.
}
} // namespace anonymous
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android