mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge changes I5a4669b1,I78613b1d into main
* changes: Refactor RecurrentTimer to use Looper. Use timestamp from property store when get prop.
This commit is contained in:
@@ -70,6 +70,16 @@ public:
|
||||
* example wasn't registered. */
|
||||
bool writeValue(const VehiclePropValue& propValue, bool updateStatus);
|
||||
|
||||
/*
|
||||
* Stores provided value. Returns true if value was written returns false if config for
|
||||
* example wasn't registered.
|
||||
*
|
||||
* The property value's timestamp will be set to the current ElapsedRealTimeNano.
|
||||
*/
|
||||
bool writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr, bool updateStatus);
|
||||
|
||||
std::unique_ptr<VehiclePropValue> refreshTimestamp(int32_t propId, int32_t areaId);
|
||||
|
||||
void removeValue(const VehiclePropValue& propValue);
|
||||
void removeValuesForProperty(int32_t propId);
|
||||
|
||||
@@ -94,6 +104,8 @@ private:
|
||||
std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;
|
||||
|
||||
PropertyMap mPropertyValues; // Sorted map of RecordId : VehiclePropValue.
|
||||
|
||||
bool writeValueLocked(const VehiclePropValue& propValue, bool updateStatus);
|
||||
};
|
||||
|
||||
} // namespace V2_0
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#define LOG_TAG "VehiclePropertyStore"
|
||||
#include <log/log.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <common/include/vhal_v2_0/VehicleUtils.h>
|
||||
#include "VehiclePropertyStore.h"
|
||||
@@ -41,9 +42,7 @@ void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
|
||||
mConfigs.insert({ config.prop, RecordConfig { config, tokenFunc } });
|
||||
}
|
||||
|
||||
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
|
||||
bool updateStatus) {
|
||||
MuxGuard g(mLock);
|
||||
bool VehiclePropertyStore::writeValueLocked(const VehiclePropValue& propValue, bool updateStatus) {
|
||||
if (!mConfigs.count(propValue.prop)) return false;
|
||||
|
||||
RecordId recId = getRecordIdLocked(propValue);
|
||||
@@ -68,6 +67,36 @@ bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue, bool updateStatus) {
|
||||
MuxGuard g(mLock);
|
||||
|
||||
return writeValueLocked(propValue, updateStatus);
|
||||
}
|
||||
|
||||
bool VehiclePropertyStore::writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr,
|
||||
bool updateStatus) {
|
||||
MuxGuard g(mLock);
|
||||
|
||||
propValuePtr->timestamp = elapsedRealtimeNano();
|
||||
return writeValueLocked(*propValuePtr, updateStatus);
|
||||
}
|
||||
|
||||
std::unique_ptr<VehiclePropValue> VehiclePropertyStore::refreshTimestamp(int32_t propId,
|
||||
int32_t areaId) {
|
||||
MuxGuard g(mLock);
|
||||
RecordId recId = getRecordIdLocked(VehiclePropValue{
|
||||
.prop = propId,
|
||||
.areaId = areaId,
|
||||
});
|
||||
auto it = mPropertyValues.find(recId);
|
||||
if (it == mPropertyValues.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
it->second.timestamp = elapsedRealtimeNano();
|
||||
return std::make_unique<VehiclePropValue>(it->second);
|
||||
}
|
||||
|
||||
void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
|
||||
MuxGuard g(mLock);
|
||||
RecordId recId = getRecordIdLocked(propValue);
|
||||
|
||||
@@ -57,12 +57,6 @@ const VehicleAreaConfig* getAreaConfig(const VehiclePropValue& propValue,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VehicleHal::VehiclePropValuePtr addTimestamp(VehicleHal::VehiclePropValuePtr v) {
|
||||
if (v.get()) {
|
||||
v->timestamp = elapsedRealtimeNano();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
|
||||
@@ -102,7 +96,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::getUserHalProp(
|
||||
*outStatus = StatusCode::INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
return addTimestamp(std::move(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
|
||||
@@ -118,13 +112,13 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r
|
||||
if (propId == OBD2_FREEZE_FRAME) {
|
||||
v = getValuePool()->obtainComplex();
|
||||
*outStatus = fillObd2FreezeFrame(mPropStore, requestedPropValue, v.get());
|
||||
return addTimestamp(std::move(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
if (propId == OBD2_FREEZE_FRAME_INFO) {
|
||||
v = getValuePool()->obtainComplex();
|
||||
*outStatus = fillObd2DtcInfo(mPropStore, v.get());
|
||||
return addTimestamp(std::move(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
|
||||
@@ -139,7 +133,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r
|
||||
} else {
|
||||
*outStatus = StatusCode::TRY_AGAIN;
|
||||
}
|
||||
return addTimestamp(std::move(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
|
||||
@@ -486,26 +480,42 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::doInternalHealthCheck() {
|
||||
|
||||
void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
|
||||
auto& pool = *getValuePool();
|
||||
|
||||
for (int32_t property : properties) {
|
||||
VehiclePropValuePtr v;
|
||||
std::vector<VehiclePropValuePtr> events;
|
||||
if (isContinuousProperty(property)) {
|
||||
auto internalPropValue = mPropStore->readValueOrNull(property);
|
||||
if (internalPropValue != nullptr) {
|
||||
v = pool.obtain(*internalPropValue);
|
||||
const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
|
||||
std::vector<int32_t> areaIds;
|
||||
if (isGlobalProp(property)) {
|
||||
areaIds.push_back(0);
|
||||
} else {
|
||||
for (auto& c : config->areaConfigs) {
|
||||
areaIds.push_back(c.areaId);
|
||||
}
|
||||
}
|
||||
|
||||
for (int areaId : areaIds) {
|
||||
auto v = pool.obtain(*mPropStore->refreshTimestamp(property, areaId));
|
||||
if (v.get()) {
|
||||
events.push_back(std::move(v));
|
||||
}
|
||||
}
|
||||
} else if (property == static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
|
||||
// VHAL_HEARTBEAT is not a continuous value, but it needs to be updated periodically.
|
||||
// So, the update is done through onContinuousPropertyTimer.
|
||||
v = doInternalHealthCheck();
|
||||
auto v = doInternalHealthCheck();
|
||||
if (!v.get()) {
|
||||
// Internal health check failed.
|
||||
continue;
|
||||
}
|
||||
mPropStore->writeValueWithCurrentTimestamp(v.get(), /*updateStatus=*/true);
|
||||
events.push_back(std::move(v));
|
||||
} else {
|
||||
ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (v.get()) {
|
||||
v->timestamp = elapsedRealtimeNano();
|
||||
doHalEvent(std::move(v));
|
||||
for (VehiclePropValuePtr& event : events) {
|
||||
doHalEvent(std::move(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -556,7 +566,7 @@ bool DefaultVehicleHal::isContinuousProperty(int32_t propId) const {
|
||||
void DefaultVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
|
||||
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
|
||||
|
||||
if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
|
||||
if (mPropStore->writeValueWithCurrentTimestamp(updatedPropValue.get(), updateStatus)) {
|
||||
doHalEvent(std::move(updatedPropValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,13 @@ using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_CLEAR;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_INFO;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_LIVE_FRAME;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_LEFT;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_RIGHT;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_LEFT;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_RIGHT;
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
|
||||
|
||||
@@ -346,6 +351,38 @@ TEST_F(DefaultVhalImplTest, testSubscribe) {
|
||||
EXPECT_EQ(1.0f, lastEvent->value.floatValues[0]);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testSubscribeContinuous_withMultipleAreaIds) {
|
||||
// Clear existing events.
|
||||
mEventQueue.flush();
|
||||
int propId = toInt(VehicleProperty::TIRE_PRESSURE);
|
||||
|
||||
auto status = mHal->subscribe(propId, 1);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
std::vector<VehiclePropValuePtr> receivedEvents;
|
||||
// Wait for 2 updates, each for 4 area IDs.
|
||||
waitForEvents(&receivedEvents, 4 * 2);
|
||||
|
||||
std::vector<int> areasForUpdate1;
|
||||
std::vector<int> areasForUpdate2;
|
||||
|
||||
for (size_t i = 0; i < receivedEvents.size(); i++) {
|
||||
ASSERT_EQ(receivedEvents[i]->prop, propId);
|
||||
|
||||
if (i < 4) {
|
||||
areasForUpdate1.push_back(receivedEvents[i]->areaId);
|
||||
} else {
|
||||
areasForUpdate2.push_back(receivedEvents[i]->areaId);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_THAT(areasForUpdate1, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
|
||||
WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
|
||||
ASSERT_THAT(areasForUpdate2, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
|
||||
WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testSubscribeInvalidProp) {
|
||||
EXPECT_EQ(StatusCode::INVALID_ARG, mHal->subscribe(toInt(VehicleProperty::INFO_MAKE), 10));
|
||||
}
|
||||
@@ -1318,7 +1355,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetInt) {
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
ASSERT_EQ((size_t)1, events[0]->value.int32Values.size());
|
||||
EXPECT_EQ(2022, events[0]->value.int32Values[0]);
|
||||
EXPECT_EQ(1000, events[0]->timestamp);
|
||||
|
||||
VehiclePropValue value;
|
||||
StatusCode status;
|
||||
@@ -1352,7 +1388,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetBool) {
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
EXPECT_EQ(0, events[0]->value.int32Values[0]);
|
||||
EXPECT_EQ(DOOR_1_LEFT, events[0]->areaId);
|
||||
EXPECT_EQ(1000, events[0]->timestamp);
|
||||
|
||||
VehiclePropValue value;
|
||||
StatusCode status;
|
||||
@@ -1391,7 +1426,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetFloat) {
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
ASSERT_EQ((size_t)1, events[0]->value.floatValues.size());
|
||||
EXPECT_EQ(10.5, events[0]->value.floatValues[0]);
|
||||
EXPECT_EQ(1000, events[0]->timestamp);
|
||||
|
||||
VehiclePropValue value;
|
||||
StatusCode status;
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <android-base/thread_annotations.h>
|
||||
|
||||
#include <utils/Looper.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
@@ -31,6 +33,9 @@ namespace hardware {
|
||||
namespace automotive {
|
||||
namespace vehicle {
|
||||
|
||||
// Forward declaration
|
||||
class RecurrentMessageHandler;
|
||||
|
||||
// A thread-safe recurrent timer.
|
||||
class RecurrentTimer final {
|
||||
public:
|
||||
@@ -43,49 +48,44 @@ class RecurrentTimer final {
|
||||
|
||||
// Registers a recurrent callback for a given interval.
|
||||
// Registering the same callback twice will override the interval provided before.
|
||||
void registerTimerCallback(int64_t intervalInNano, std::shared_ptr<Callback> callback);
|
||||
void registerTimerCallback(int64_t intervalInNanos, std::shared_ptr<Callback> callback);
|
||||
|
||||
// Unregisters a previously registered recurrent callback.
|
||||
void unregisterTimerCallback(std::shared_ptr<Callback> callback);
|
||||
|
||||
private:
|
||||
// friend class for unit testing.
|
||||
friend class RecurrentMessageHandler;
|
||||
|
||||
// For unit test
|
||||
friend class RecurrentTimerTest;
|
||||
|
||||
struct CallbackInfo {
|
||||
std::shared_ptr<Callback> callback;
|
||||
int64_t interval;
|
||||
int64_t nextTime;
|
||||
// A flag to indicate whether this CallbackInfo is already outdated and should be ignored.
|
||||
// The reason we need this flag is because we cannot easily remove an element from a heap.
|
||||
bool outdated = false;
|
||||
|
||||
static bool cmp(const std::unique_ptr<CallbackInfo>& lhs,
|
||||
const std::unique_ptr<CallbackInfo>& rhs);
|
||||
int64_t intervalInNanos;
|
||||
int64_t nextTimeInNanos;
|
||||
};
|
||||
|
||||
android::sp<Looper> mLooper;
|
||||
android::sp<RecurrentMessageHandler> mHandler;
|
||||
|
||||
std::atomic<bool> mStopRequested = false;
|
||||
std::atomic<int> mCallbackId = 0;
|
||||
std::mutex mLock;
|
||||
std::thread mThread;
|
||||
std::condition_variable mCond;
|
||||
bool mStopRequested GUARDED_BY(mLock) = false;
|
||||
// A map to map each callback to its current active CallbackInfo in the mCallbackQueue.
|
||||
std::unordered_map<std::shared_ptr<Callback>, CallbackInfo*> mCallbacks GUARDED_BY(mLock);
|
||||
// A min-heap sorted by nextTime. Note that because we cannot remove arbitrary element from the
|
||||
// heap, a single Callback can have multiple entries in this queue, all but one should be valid.
|
||||
// The rest should be mark as outdated. The valid one is one stored in mCallbacks.
|
||||
std::vector<std::unique_ptr<CallbackInfo>> mCallbackQueue GUARDED_BY(mLock);
|
||||
std::unordered_map<std::shared_ptr<Callback>, int> mIdByCallback GUARDED_BY(mLock);
|
||||
std::unordered_map<int, std::unique_ptr<CallbackInfo>> mCallbackInfoById GUARDED_BY(mLock);
|
||||
|
||||
void loop();
|
||||
void handleMessage(const android::Message& message) EXCLUDES(mLock);
|
||||
int getCallbackIdLocked(std::shared_ptr<Callback> callback) REQUIRES(mLock);
|
||||
};
|
||||
|
||||
// Mark the callbackInfo as outdated and should be ignored when popped from the heap.
|
||||
void markOutdatedLocked(CallbackInfo* callback) REQUIRES(mLock);
|
||||
// Remove all outdated callbackInfos from the top of the heap. This function must be called
|
||||
// each time we might introduce outdated elements to the top. We must make sure the heap is
|
||||
// always valid from the top.
|
||||
void removeInvalidCallbackLocked() REQUIRES(mLock);
|
||||
// Gets the next calblack to run (must be valid) from the heap, update its nextTime and put
|
||||
// it back to the heap.
|
||||
std::shared_ptr<Callback> getNextCallbackLocked(int64_t now) REQUIRES(mLock);
|
||||
class RecurrentMessageHandler final : public android::MessageHandler {
|
||||
public:
|
||||
RecurrentMessageHandler(RecurrentTimer* timer) { mTimer = timer; }
|
||||
void handleMessage(const android::Message& message) override;
|
||||
|
||||
private:
|
||||
RecurrentTimer* mTimer;
|
||||
};
|
||||
|
||||
} // namespace vehicle
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "RecurrentTimer.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
@@ -27,153 +28,119 @@ namespace hardware {
|
||||
namespace automotive {
|
||||
namespace vehicle {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::android::base::ScopedLockAssertion;
|
||||
|
||||
constexpr int INVALID_ID = -1;
|
||||
|
||||
} // namespace
|
||||
|
||||
RecurrentTimer::RecurrentTimer() {
|
||||
mThread = std::thread(&RecurrentTimer::loop, this);
|
||||
mHandler = sp<RecurrentMessageHandler>::make(this);
|
||||
mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
|
||||
mThread = std::thread([this] {
|
||||
Looper::setForThread(mLooper);
|
||||
|
||||
while (!mStopRequested) {
|
||||
mLooper->pollOnce(/*timeoutMillis=*/-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RecurrentTimer::~RecurrentTimer() {
|
||||
{
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
mStopRequested = true;
|
||||
}
|
||||
mCond.notify_one();
|
||||
mStopRequested = true;
|
||||
mLooper->removeMessages(mHandler);
|
||||
mLooper->wake();
|
||||
if (mThread.joinable()) {
|
||||
mThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void RecurrentTimer::registerTimerCallback(int64_t intervalInNano,
|
||||
int RecurrentTimer::getCallbackIdLocked(std::shared_ptr<RecurrentTimer::Callback> callback) {
|
||||
const auto& it = mIdByCallback.find(callback);
|
||||
if (it != mIdByCallback.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return INVALID_ID;
|
||||
}
|
||||
|
||||
void RecurrentTimer::registerTimerCallback(int64_t intervalInNanos,
|
||||
std::shared_ptr<RecurrentTimer::Callback> callback) {
|
||||
{
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
|
||||
int callbackId = getCallbackIdLocked(callback);
|
||||
|
||||
if (callbackId == INVALID_ID) {
|
||||
callbackId = mCallbackId++;
|
||||
mIdByCallback.insert({callback, callbackId});
|
||||
} else {
|
||||
ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
|
||||
" ns, new: %" PRId64 " ns",
|
||||
mCallbackInfoById[callbackId]->intervalInNanos, intervalInNanos);
|
||||
mLooper->removeMessages(mHandler, callbackId);
|
||||
}
|
||||
|
||||
// Aligns the nextTime to multiply of interval.
|
||||
int64_t nextTime = ceil(uptimeNanos() / intervalInNano) * intervalInNano;
|
||||
int64_t nextTimeInNanos = ceil(uptimeNanos() / intervalInNanos) * intervalInNanos;
|
||||
|
||||
std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
|
||||
info->callback = callback;
|
||||
info->interval = intervalInNano;
|
||||
info->nextTime = nextTime;
|
||||
info->intervalInNanos = intervalInNanos;
|
||||
info->nextTimeInNanos = nextTimeInNanos;
|
||||
mCallbackInfoById.insert({callbackId, std::move(info)});
|
||||
|
||||
auto it = mCallbacks.find(callback);
|
||||
if (it != mCallbacks.end()) {
|
||||
ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
|
||||
" ns, new: %" PRId64 " ns",
|
||||
it->second->interval, intervalInNano);
|
||||
markOutdatedLocked(it->second);
|
||||
}
|
||||
mCallbacks[callback] = info.get();
|
||||
mCallbackQueue.push_back(std::move(info));
|
||||
// Insert the last element into the heap.
|
||||
std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
|
||||
mLooper->sendMessageAtTime(nextTimeInNanos, mHandler, Message(callbackId));
|
||||
}
|
||||
mCond.notify_one();
|
||||
}
|
||||
|
||||
void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
|
||||
{
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
|
||||
auto it = mCallbacks.find(callback);
|
||||
if (it == mCallbacks.end()) {
|
||||
int callbackId = getCallbackIdLocked(callback);
|
||||
|
||||
if (callbackId == INVALID_ID) {
|
||||
ALOGE("No event found to unregister");
|
||||
return;
|
||||
}
|
||||
|
||||
markOutdatedLocked(it->second);
|
||||
mCallbacks.erase(it);
|
||||
}
|
||||
|
||||
mCond.notify_one();
|
||||
}
|
||||
|
||||
void RecurrentTimer::markOutdatedLocked(RecurrentTimer::CallbackInfo* info) {
|
||||
info->outdated = true;
|
||||
info->callback = nullptr;
|
||||
// Make sure the first element is always valid.
|
||||
removeInvalidCallbackLocked();
|
||||
}
|
||||
|
||||
void RecurrentTimer::removeInvalidCallbackLocked() {
|
||||
while (mCallbackQueue.size() != 0 && mCallbackQueue[0]->outdated) {
|
||||
std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
|
||||
mCallbackQueue.pop_back();
|
||||
mLooper->removeMessages(mHandler, callbackId);
|
||||
mCallbackInfoById.erase(callbackId);
|
||||
mIdByCallback.erase(callback);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RecurrentTimer::Callback> RecurrentTimer::getNextCallbackLocked(int64_t now) {
|
||||
std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
|
||||
auto& callbackInfo = mCallbackQueue[mCallbackQueue.size() - 1];
|
||||
auto nextCallback = callbackInfo->callback;
|
||||
// intervalCount is the number of interval we have to advance until we pass now.
|
||||
size_t intervalCount = (now - callbackInfo->nextTime) / callbackInfo->interval + 1;
|
||||
callbackInfo->nextTime += intervalCount * callbackInfo->interval;
|
||||
std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
|
||||
void RecurrentTimer::handleMessage(const Message& message) {
|
||||
std::shared_ptr<RecurrentTimer::Callback> callback;
|
||||
{
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
|
||||
// Make sure the first element is always valid.
|
||||
removeInvalidCallbackLocked();
|
||||
int callbackId = message.what;
|
||||
|
||||
return nextCallback;
|
||||
}
|
||||
|
||||
void RecurrentTimer::loop() {
|
||||
std::vector<std::shared_ptr<Callback>> callbacksToRun;
|
||||
while (true) {
|
||||
{
|
||||
std::unique_lock<std::mutex> uniqueLock(mLock);
|
||||
ScopedLockAssertion lockAssertion(mLock);
|
||||
// Wait until the timer exits or we have at least one recurrent callback.
|
||||
mCond.wait(uniqueLock, [this] {
|
||||
ScopedLockAssertion lockAssertion(mLock);
|
||||
return mStopRequested || mCallbackQueue.size() != 0;
|
||||
});
|
||||
|
||||
int64_t interval;
|
||||
if (mStopRequested) {
|
||||
return;
|
||||
}
|
||||
// The first element is the nearest next event.
|
||||
int64_t nextTime = mCallbackQueue[0]->nextTime;
|
||||
int64_t now = uptimeNanos();
|
||||
|
||||
if (nextTime > now) {
|
||||
interval = nextTime - now;
|
||||
} else {
|
||||
interval = 0;
|
||||
}
|
||||
|
||||
// Wait for the next event or the timer exits.
|
||||
if (mCond.wait_for(uniqueLock, std::chrono::nanoseconds(interval), [this] {
|
||||
ScopedLockAssertion lockAssertion(mLock);
|
||||
return mStopRequested;
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
now = uptimeNanos();
|
||||
callbacksToRun.clear();
|
||||
while (mCallbackQueue.size() > 0) {
|
||||
int64_t nextTime = mCallbackQueue[0]->nextTime;
|
||||
if (nextTime > now) {
|
||||
break;
|
||||
}
|
||||
|
||||
callbacksToRun.push_back(getNextCallbackLocked(now));
|
||||
}
|
||||
auto it = mCallbackInfoById.find(callbackId);
|
||||
if (it == mCallbackInfoById.end()) {
|
||||
ALOGW("The event for callback ID: %d is outdated, ignore", callbackId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not execute the callback while holding the lock.
|
||||
for (size_t i = 0; i < callbacksToRun.size(); i++) {
|
||||
(*callbacksToRun[i])();
|
||||
}
|
||||
CallbackInfo* callbackInfo = it->second.get();
|
||||
callback = callbackInfo->callback;
|
||||
int64_t nowNanos = uptimeNanos();
|
||||
// intervalCount is the number of interval we have to advance until we pass now.
|
||||
size_t intervalCount =
|
||||
(nowNanos - callbackInfo->nextTimeInNanos) / callbackInfo->intervalInNanos + 1;
|
||||
callbackInfo->nextTimeInNanos += intervalCount * callbackInfo->intervalInNanos;
|
||||
|
||||
mLooper->sendMessageAtTime(callbackInfo->nextTimeInNanos, mHandler, Message(callbackId));
|
||||
}
|
||||
|
||||
(*callback)();
|
||||
}
|
||||
|
||||
bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs,
|
||||
const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) {
|
||||
return lhs->nextTime > rhs->nextTime;
|
||||
void RecurrentMessageHandler::handleMessage(const Message& message) {
|
||||
mTimer->handleMessage(message);
|
||||
}
|
||||
|
||||
} // namespace vehicle
|
||||
|
||||
@@ -60,9 +60,14 @@ class RecurrentTimerTest : public testing::Test {
|
||||
mCallbacks.clear();
|
||||
}
|
||||
|
||||
size_t countTimerCallbackQueue(RecurrentTimer* timer) {
|
||||
size_t countCallbackInfoById(RecurrentTimer* timer) {
|
||||
std::scoped_lock<std::mutex> lockGuard(timer->mLock);
|
||||
return timer->mCallbackQueue.size();
|
||||
return timer->mCallbackInfoById.size();
|
||||
}
|
||||
|
||||
size_t countIdByCallback(RecurrentTimer* timer) {
|
||||
std::scoped_lock<std::mutex> lockGuard(timer->mLock);
|
||||
return timer->mIdByCallback.size();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -109,6 +114,9 @@ TEST_F(RecurrentTimerTest, testRegisterUnregisterRegister) {
|
||||
<< "Not enough callbacks called before timeout";
|
||||
|
||||
timer.unregisterTimerCallback(action);
|
||||
|
||||
ASSERT_EQ(countCallbackInfoById(&timer), 0u);
|
||||
ASSERT_EQ(countIdByCallback(&timer), 0u);
|
||||
}
|
||||
|
||||
TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) {
|
||||
@@ -198,8 +206,8 @@ TEST_F(RecurrentTimerTest, testRegisterSameCallbackMultipleTimes) {
|
||||
|
||||
timer.unregisterTimerCallback(action);
|
||||
|
||||
// Make sure there is no item in the callback queue.
|
||||
ASSERT_EQ(countTimerCallbackQueue(&timer), static_cast<size_t>(0));
|
||||
ASSERT_EQ(countCallbackInfoById(&timer), 0u);
|
||||
ASSERT_EQ(countIdByCallback(&timer), 0u);
|
||||
}
|
||||
|
||||
TEST_F(RecurrentTimerTest, testRegisterCallbackMultipleTimesNoDeadLock) {
|
||||
|
||||
@@ -59,6 +59,7 @@ using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
|
||||
using ::aidl::android::hardware::automotive::vehicle::SetValueResults;
|
||||
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
|
||||
using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
|
||||
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
|
||||
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
|
||||
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
|
||||
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs;
|
||||
|
||||
Reference in New Issue
Block a user