• 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() {
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