1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "RecurrentTimer.h"
18
19 #include <utils/Log.h>
20 #include <utils/SystemClock.h>
21
22 #include <inttypes.h>
23 #include <math.h>
24
25 namespace android {
26 namespace hardware {
27 namespace automotive {
28 namespace vehicle {
29
30 using ::android::base::ScopedLockAssertion;
31
RecurrentTimer()32 RecurrentTimer::RecurrentTimer() {
33 mThread = std::thread(&RecurrentTimer::loop, this);
34 }
35
~RecurrentTimer()36 RecurrentTimer::~RecurrentTimer() {
37 {
38 std::scoped_lock<std::mutex> lockGuard(mLock);
39 mStopRequested = true;
40 }
41 mCond.notify_one();
42 if (mThread.joinable()) {
43 mThread.join();
44 }
45 }
46
registerTimerCallback(int64_t intervalInNano,std::shared_ptr<RecurrentTimer::Callback> callback)47 void RecurrentTimer::registerTimerCallback(int64_t intervalInNano,
48 std::shared_ptr<RecurrentTimer::Callback> callback) {
49 {
50 std::scoped_lock<std::mutex> lockGuard(mLock);
51
52 // Aligns the nextTime to multiply of interval.
53 int64_t nextTime = ceil(uptimeNanos() / intervalInNano) * intervalInNano;
54
55 std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
56 info->callback = callback;
57 info->interval = intervalInNano;
58 info->nextTime = nextTime;
59
60 auto it = mCallbacks.find(callback);
61 if (it != mCallbacks.end()) {
62 ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
63 " ns, new: %" PRId64 " ns",
64 it->second->interval, intervalInNano);
65 markOutdatedLocked(it->second);
66 }
67 mCallbacks[callback] = info.get();
68 mCallbackQueue.push_back(std::move(info));
69 // Insert the last element into the heap.
70 std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
71 }
72 mCond.notify_one();
73 }
74
unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback)75 void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
76 {
77 std::scoped_lock<std::mutex> lockGuard(mLock);
78
79 auto it = mCallbacks.find(callback);
80 if (it == mCallbacks.end()) {
81 ALOGE("No event found to unregister");
82 return;
83 }
84
85 markOutdatedLocked(it->second);
86 mCallbacks.erase(it);
87 }
88
89 mCond.notify_one();
90 }
91
markOutdatedLocked(RecurrentTimer::CallbackInfo * info)92 void RecurrentTimer::markOutdatedLocked(RecurrentTimer::CallbackInfo* info) {
93 info->outdated = true;
94 info->callback = nullptr;
95 // Make sure the first element is always valid.
96 removeInvalidCallbackLocked();
97 }
98
removeInvalidCallbackLocked()99 void RecurrentTimer::removeInvalidCallbackLocked() {
100 while (mCallbackQueue.size() != 0 && mCallbackQueue[0]->outdated) {
101 std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
102 mCallbackQueue.pop_back();
103 }
104 }
105
getNextCallbackLocked(int64_t now)106 std::shared_ptr<RecurrentTimer::Callback> RecurrentTimer::getNextCallbackLocked(int64_t now) {
107 std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
108 auto& callbackInfo = mCallbackQueue[mCallbackQueue.size() - 1];
109 auto nextCallback = callbackInfo->callback;
110 // intervalCount is the number of interval we have to advance until we pass now.
111 size_t intervalCount = (now - callbackInfo->nextTime) / callbackInfo->interval + 1;
112 callbackInfo->nextTime += intervalCount * callbackInfo->interval;
113 std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
114
115 // Make sure the first element is always valid.
116 removeInvalidCallbackLocked();
117
118 return nextCallback;
119 }
120
loop()121 void RecurrentTimer::loop() {
122 std::vector<std::shared_ptr<Callback>> callbacksToRun;
123 while (true) {
124 {
125 std::unique_lock<std::mutex> uniqueLock(mLock);
126 ScopedLockAssertion lockAssertion(mLock);
127 // Wait until the timer exits or we have at least one recurrent callback.
128 mCond.wait(uniqueLock, [this] {
129 ScopedLockAssertion lockAssertion(mLock);
130 return mStopRequested || mCallbackQueue.size() != 0;
131 });
132
133 int64_t interval;
134 if (mStopRequested) {
135 return;
136 }
137 // The first element is the nearest next event.
138 int64_t nextTime = mCallbackQueue[0]->nextTime;
139 int64_t now = uptimeNanos();
140
141 if (nextTime > now) {
142 interval = nextTime - now;
143 } else {
144 interval = 0;
145 }
146
147 // Wait for the next event or the timer exits.
148 if (mCond.wait_for(uniqueLock, std::chrono::nanoseconds(interval), [this] {
149 ScopedLockAssertion lockAssertion(mLock);
150 return mStopRequested;
151 })) {
152 return;
153 }
154
155 now = uptimeNanos();
156 callbacksToRun.clear();
157 while (mCallbackQueue.size() > 0) {
158 int64_t nextTime = mCallbackQueue[0]->nextTime;
159 if (nextTime > now) {
160 break;
161 }
162
163 callbacksToRun.push_back(getNextCallbackLocked(now));
164 }
165 }
166
167 // Do not execute the callback while holding the lock.
168 for (size_t i = 0; i < callbacksToRun.size(); i++) {
169 (*callbacksToRun[i])();
170 }
171 }
172 }
173
cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo> & lhs,const std::unique_ptr<RecurrentTimer::CallbackInfo> & rhs)174 bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs,
175 const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) {
176 return lhs->nextTime > rhs->nextTime;
177 }
178
179 } // namespace vehicle
180 } // namespace automotive
181 } // namespace hardware
182 } // namespace android
183