summaryrefslogtreecommitdiff
path: root/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
diff options
context:
space:
mode:
authorYu Shan <shanyu@google.com>2022-03-23 22:21:14 +0000
committerYu Shan <shanyu@google.com>2022-04-06 23:40:17 +0000
commit5a06465d3c697bb27a4b2d28b53d02984be2c150 (patch)
tree240c00caa987c4e2e22b360cdcd122354998c843 /automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
parent237cd1c054005a17171385079708c732cbdf3493 (diff)
Update subscription logic for VHAL ref impl.
Update the implementation for subscription logic. Add clearer documentation for sampleRate and timestamp behavior. The sampleRate specified in subscribeOptions is just a guidance to tell VHAL what the polling rate could be. For timestamp, the timestamp returned for each property must be the timestamp when that property is updated, not when the property is retrieved. Test: atest FakeVehicleHardwareTest Bug: 225191802, 226000926 Change-Id: I1e886133258236eedfa7fcffe5c4fb49aead4f6f
Diffstat (limited to 'automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp')
-rw-r--r--automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp177
1 files changed, 177 insertions, 0 deletions
diff --git a/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
new file mode 100644
index 0000000000..8521c4db7c
--- /dev/null
+++ b/automotive/vehicle/aidl/impl/utils/common/src/RecurrentTimer.cpp
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ */
+
+#include "RecurrentTimer.h"
+
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <inttypes.h>
+#include <math.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace vehicle {
+
+using ::android::base::ScopedLockAssertion;
+
+RecurrentTimer::RecurrentTimer() : mThread(&RecurrentTimer::loop, this) {}
+
+RecurrentTimer::~RecurrentTimer() {
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mStopRequested = true;
+ }
+ mCond.notify_one();
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void RecurrentTimer::registerTimerCallback(int64_t intervalInNano,
+ std::shared_ptr<RecurrentTimer::Callback> callback) {
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+
+ // Aligns the nextTime to multiply of interval.
+ int64_t nextTime = ceil(elapsedRealtimeNano() / intervalInNano) * intervalInNano;
+
+ std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
+ info->callback = callback;
+ info->interval = intervalInNano;
+ info->nextTime = nextTime;
+
+ 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);
+ }
+ 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()) {
+ 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();
+ }
+}
+
+std::unique_ptr<RecurrentTimer::CallbackInfo> RecurrentTimer::popNextCallbackLocked() {
+ std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
+ std::unique_ptr<CallbackInfo> info = std::move(mCallbackQueue[mCallbackQueue.size() - 1]);
+ mCallbackQueue.pop_back();
+ // Make sure the first element is always valid.
+ removeInvalidCallbackLocked();
+ return info;
+}
+
+void RecurrentTimer::loop() {
+ std::unique_lock<std::mutex> uniqueLock(mLock);
+
+ while (true) {
+ // 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;
+ {
+ ScopedLockAssertion lockAssertion(mLock);
+ if (mStopRequested) {
+ return;
+ }
+ // The first element is the nearest next event.
+ int64_t nextTime = mCallbackQueue[0]->nextTime;
+ int64_t now = elapsedRealtimeNano();
+ 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;
+ }
+
+ {
+ ScopedLockAssertion lockAssertion(mLock);
+ int64_t now = elapsedRealtimeNano();
+ while (mCallbackQueue.size() > 0) {
+ int64_t nextTime = mCallbackQueue[0]->nextTime;
+ if (nextTime > now) {
+ break;
+ }
+
+ std::unique_ptr<CallbackInfo> info = popNextCallbackLocked();
+ info->nextTime += info->interval;
+
+ auto callback = info->callback;
+ mCallbackQueue.push_back(std::move(info));
+ std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
+
+ (*callback)();
+ }
+ }
+ }
+}
+
+bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs,
+ const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) {
+ return lhs->nextTime > rhs->nextTime;
+}
+
+} // namespace vehicle
+} // namespace automotive
+} // namespace hardware
+} // namespace android