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() : mThread(&RecurrentTimer::loop, this) {}
33
~RecurrentTimer()34 RecurrentTimer::~RecurrentTimer() {
35 {
36 std::scoped_lock<std::mutex> lockGuard(mLock);
37 mStopRequested = true;
38 }
39 mCond.notify_one();
40 if (mThread.joinable()) {
41 mThread.join();
42 }
43 }
44
registerTimerCallback(int64_t intervalInNano,std::shared_ptr<RecurrentTimer::Callback> callback)45 void RecurrentTimer::registerTimerCallback(int64_t intervalInNano,
46 std::shared_ptr<RecurrentTimer::Callback> callback) {
47 {
48 std::scoped_lock<std::mutex> lockGuard(mLock);
49
50 // Aligns the nextTime to multiply of interval.
51 int64_t nextTime = ceil(uptimeNanos() / intervalInNano) * intervalInNano;
52
53 std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
54 info->callback = callback;
55 info->interval = intervalInNano;
56 info->nextTime = nextTime;
57
58 auto it = mCallbacks.find(callback);
59 if (it != mCallbacks.end()) {
60 ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
61 " ns, new: %" PRId64 " ns",
62 it->second->interval, intervalInNano);
63 markOutdatedLocked(it->second);
64 }
65 mCallbacks[callback] = info.get();
66 mCallbackQueue.push_back(std::move(info));
67 // Insert the last element into the heap.
68 std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
69 }
70 mCond.notify_one();
71 }
72
unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback)73 void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
74 {
75 std::scoped_lock<std::mutex> lockGuard(mLock);
76
77 auto it = mCallbacks.find(callback);
78 if (it == mCallbacks.end()) {
79 ALOGE("No event found to unregister");
80 return;
81 }
82
83 markOutdatedLocked(it->second);
84 mCallbacks.erase(it);
85 }
86
87 mCond.notify_one();
88 }
89
markOutdatedLocked(RecurrentTimer::CallbackInfo * info)90 void RecurrentTimer::markOutdatedLocked(RecurrentTimer::CallbackInfo* info) {
91 info->outdated = true;
92 info->callback = nullptr;
93 // Make sure the first element is always valid.
94 removeInvalidCallbackLocked();
95 }
96
removeInvalidCallbackLocked()97 void RecurrentTimer::removeInvalidCallbackLocked() {
98 while (mCallbackQueue.size() != 0 && mCallbackQueue[0]->outdated) {
99 std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
100 mCallbackQueue.pop_back();
101 }
102 }
103
getNextCallbackLocked(int64_t now)104 std::shared_ptr<RecurrentTimer::Callback> RecurrentTimer::getNextCallbackLocked(int64_t now) {
105 std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
106 auto& callbackInfo = mCallbackQueue[mCallbackQueue.size() - 1];
107 auto nextCallback = callbackInfo->callback;
108 // intervalCount is the number of interval we have to advance until we pass now.
109 size_t intervalCount = (now - callbackInfo->nextTime) / callbackInfo->interval + 1;
110 callbackInfo->nextTime += intervalCount * callbackInfo->interval;
111 std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
112
113 // Make sure the first element is always valid.
114 removeInvalidCallbackLocked();
115
116 return nextCallback;
117 }
118
loop()119 void RecurrentTimer::loop() {
120 std::vector<std::shared_ptr<Callback>> callbacksToRun;
121 while (true) {
122 {
123 std::unique_lock<std::mutex> uniqueLock(mLock);
124 ScopedLockAssertion lockAssertion(mLock);
125 // Wait until the timer exits or we have at least one recurrent callback.
126 mCond.wait(uniqueLock, [this] {
127 ScopedLockAssertion lockAssertion(mLock);
128 return mStopRequested || mCallbackQueue.size() != 0;
129 });
130
131 int64_t interval;
132 if (mStopRequested) {
133 return;
134 }
135 // The first element is the nearest next event.
136 int64_t nextTime = mCallbackQueue[0]->nextTime;
137 int64_t now = uptimeNanos();
138
139 if (nextTime > now) {
140 interval = nextTime - now;
141 } else {
142 interval = 0;
143 }
144
145 // Wait for the next event or the timer exits.
146 if (mCond.wait_for(uniqueLock, std::chrono::nanoseconds(interval), [this] {
147 ScopedLockAssertion lockAssertion(mLock);
148 return mStopRequested;
149 })) {
150 return;
151 }
152
153 now = uptimeNanos();
154 callbacksToRun.clear();
155 while (mCallbackQueue.size() > 0) {
156 int64_t nextTime = mCallbackQueue[0]->nextTime;
157 if (nextTime > now) {
158 break;
159 }
160
161 callbacksToRun.push_back(getNextCallbackLocked(now));
162 }
163 }
164
165 // Do not execute the callback while holding the lock.
166 for (size_t i = 0; i < callbacksToRun.size(); i++) {
167 (*callbacksToRun[i])();
168 }
169 }
170 }
171
cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo> & lhs,const std::unique_ptr<RecurrentTimer::CallbackInfo> & rhs)172 bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs,
173 const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) {
174 return lhs->nextTime > rhs->nextTime;
175 }
176
177 } // namespace vehicle
178 } // namespace automotive
179 } // namespace hardware
180 } // namespace android
181