• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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