• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "timer_ring.h"
17 
18 #include <chrono>
19 #include <thread>
20 #include <unistd.h>
21 
22 #include "xcollie_define.h"
23 #include "xcollie_utils.h"
24 
25 namespace OHOS {
26 namespace HiviewDFX {
TimerRing()27 TimerRing::TimerRing() : threadInSleep_(false), lastTimeout_(0), lastDuration_(0), ringPos_(-1)
28 {
29     // 1. init nodes, add node to free list
30     freeTaskNodes_.clear();
31     for (unsigned int i = 0; i < MAX_XCOLLIE_NUM; i++) {
32         struct TaskNode* node = &taskNodes_[i];
33         InitTaskNode(node, i);
34         freeTaskNodes_.push_back(node);
35     }
36 }
37 
~TimerRing()38 TimerRing::~TimerRing()
39 {
40     XCOLLIE_LOGI("~TimerRing exit...");
41 }
42 
ReadyToWork()43 bool TimerRing::ReadyToWork()
44 {
45     ringPos_ = 0;
46     return true;
47 }
48 
InitTaskNode(struct TaskNode * taskNode,unsigned int seq)49 void TimerRing::InitTaskNode(struct TaskNode* taskNode, unsigned int seq)
50 {
51     taskNode->id = INVALID_ID;
52     taskNode->seq = seq;
53     taskNode->pos = 0;
54     taskNode->round = 0;
55     taskNode->timerTask.name = "";
56     taskNode->timerTask.timeout = 0;
57     taskNode->timerTask.loop = false;
58     taskNode->timerTask.func = nullptr;
59     taskNode->timerTask.inputTimerPara = nullptr;
60 }
61 
Run()62 bool TimerRing::Run()
63 {
64     // sleep interval
65     if (lastDuration_ < (TIMER_RING_CHECK_INTERVAL * MILLI_SECONDS)) {
66         std::this_thread::sleep_for(std::chrono::milliseconds(
67             (TIMER_RING_CHECK_INTERVAL * MILLI_SECONDS) - lastDuration_));
68     }
69 
70     lastDuration_ = 0;
71 
72     // find task and do time out
73     auto start = std::chrono::system_clock::now();
74     DoTimeoutTask();
75     auto end = std::chrono::system_clock::now();
76     auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
77     lastDuration_ += duration.count();
78 
79     // if no task, then sleep
80     start = std::chrono::system_clock::now();
81     TrySleep();
82     end = std::chrono::system_clock::now();
83     duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
84     lastDuration_ += duration.count();
85 
86     return true;
87 }
88 
TryRecycle(std::list<struct TaskNode * > & timeoutNodes)89 void TimerRing::TryRecycle(std::list<struct TaskNode*> &timeoutNodes)
90 {
91     std::for_each(timeoutNodes.begin(), timeoutNodes.end(), [&](struct TaskNode* &node) {
92         if (node->timerTask.loop) {
93             /* if need loop, add to ring again */
94             node->pos = CALC_RING_POS(node->timerTask.timeout);
95             node->round = CALC_RING_ROUND(node->timerTask.timeout);
96             ringTaskNodes_[node->pos].push_back(node);
97         } else {
98             /* if no loop, add to free list */
99             InitTaskNode(node, node->seq);
100             freeTaskNodes_.push_back(node);
101         }
102     });
103 }
104 
DoTimeoutTask()105 void TimerRing::DoTimeoutTask()
106 {
107     std::list<struct TaskNode*> timeoutNodes;
108     {
109         std::lock_guard<std::mutex> lock(lock_);
110         for (auto it = ringTaskNodes_[ringPos_].begin(); it != ringTaskNodes_[ringPos_].end();) {
111             struct TaskNode* node = *it;
112             if (node->round == 0) {
113                 node->pos = -1;
114                 timeoutNodes.push_back(node);
115                 it = ringTaskNodes_[ringPos_].erase(it);
116             } else {
117                 node->round--;
118                 it++;
119             }
120         }
121         if (timeoutNodes.size() > 0) {
122             lastTimeout_ = time(nullptr);
123         }
124     }
125 
126     /* run timeout callback */
127     for (auto it : timeoutNodes) {
128         if (it->timerTask.func) {
129             XCOLLIE_LOGD("Trigger %s:0x%x Callback Function ... ", it->timerTask.name.c_str(), it->id);
130             it->timerTask.func(it->timerTask.inputTimerPara);
131         }
132     }
133 
134     /* try recycle and do next loop */
135     std::lock_guard<std::mutex> lock(lock_);
136     TryRecycle(timeoutNodes);
137 
138     /* point to the next timeout node */
139     ringPos_ = CALC_RING_POS(TIMER_RING_CHECK_INTERVAL);
140 }
141 
TrySleep()142 void TimerRing::TrySleep()
143 {
144     std::unique_lock<std::mutex> lock(lock_);
145     if ((freeTaskNodes_.size() == MAX_XCOLLIE_NUM) &&
146         (difftime(time(nullptr), lastTimeout_) > TIMER_RING_CHECK_INTERVAL * MAX_DELAY_COUNT)) {
147         threadInSleep_ = true;
148         condition_.wait(lock, [this] {
149             return threadInSleep_ == false;
150         });
151     }
152 }
153 
TryNotify()154 void TimerRing::TryNotify()
155 {
156     std::unique_lock<std::mutex> lock(lock_);
157     if (threadInSleep_) {
158         threadInSleep_ = false;
159         condition_.notify_one();
160     }
161 }
162 
AddTask(const struct TimerTask & task)163 int TimerRing::AddTask(const struct TimerTask &task)
164 {
165     std::unique_lock<std::mutex> lock(lock_);
166     // 1. find task from freelist
167     if (task.timeout == 0) {
168         XCOLLIE_LOGE("timeout %u invalid, add %s failed", task.timeout, task.name.c_str());
169         return INVALID_ID;
170     }
171     if (freeTaskNodes_.empty()) {
172         XCOLLIE_LOGE("no free task node, add %s failed", task.name.c_str());
173         return INVALID_ID;
174     }
175     struct TaskNode* taskNode = freeTaskNodes_.front();
176     freeTaskNodes_.pop_front();
177     // 2. init task
178     time_t timestamp = time(nullptr);
179     taskNode->id = (((static_cast<unsigned int>(timestamp) % MAX_XCOLLIE_NUM) << COOKIE_SHIFT) | taskNode->seq);
180     taskNode->timerTask.timeout = task.timeout;
181     taskNode->timerTask.name = task.name;
182     taskNode->timerTask.inputTimerPara = task.inputTimerPara;
183     taskNode->timerTask.func = task.func;
184     taskNode->timerTask.loop = task.loop;
185     unsigned int timeout = ((task.timeout < TIMER_RING_CHECK_INTERVAL) ? TIMER_RING_CHECK_INTERVAL : task.timeout);
186     taskNode->round = CALC_RING_ROUND(timeout);
187     taskNode->pos = CALC_RING_POS(timeout);
188     if (threadInSleep_) {
189         taskNode->pos = CALC_RING_POS(timeout - TIMER_RING_CHECK_INTERVAL);
190     }
191     // 3. add task to timering
192     XCOLLIE_LOGD("TimerRing::AddTask ringPos_=%d, pos=%d, round=%d, timeout=%u, id =0x%x, name=%s",
193         ringPos_, taskNode->pos, taskNode->round, timeout,
194         taskNode->id, taskNode->timerTask.name.c_str());
195     ringTaskNodes_[taskNode->pos].push_back(taskNode);
196 
197     /* if thread in sleep, then notify */
198     if (threadInSleep_) {
199         threadInSleep_ = false;
200         condition_.notify_one();
201     }
202     return taskNode->id;
203 }
204 
CancelTask(int id)205 struct InputTimerPara* TimerRing::CancelTask(int id)
206 {
207     if (id <= INVALID_ID) {
208         return nullptr;
209     }
210     // 1. find tasknode
211     unsigned int seq = WRAP_SEQ(static_cast<unsigned int>(id));
212     if (seq >= MAX_XCOLLIE_NUM) {
213         XCOLLIE_LOGE("cancel task failed, seq %u limit, id 0x%x valid", seq, id);
214         return nullptr;
215     }
216 
217     // 2.  move to free task list
218     std::lock_guard<std::mutex> lock(lock_);
219     struct TaskNode* taskNode = &taskNodes_[seq];
220     if ((taskNode->id != id) || (taskNode->pos == -1)) {
221         XCOLLIE_LOGW("already release id 0x%x", id);
222         return nullptr;
223     }
224     XCOLLIE_LOGD("TimerRing::CancelTask ringPos_=%d, pos=%d id =0x%x,name=%s",  ringPos_,
225         taskNode->pos, taskNode->id, taskNode->timerTask.name.c_str());
226     ringTaskNodes_[taskNode->pos].remove(taskNode);
227     struct InputTimerPara* inputPara = taskNode->timerTask.inputTimerPara;
228     InitTaskNode(taskNode, taskNode->seq);
229     freeTaskNodes_.push_back(taskNode);
230     lastTimeout_ = time(nullptr);
231     return inputPara;
232 }
233 
UpdateTask(int id,unsigned int timeout)234 bool TimerRing::UpdateTask(int id, unsigned int timeout)
235 {
236     if (id <= INVALID_ID) {
237         return false;
238     }
239     // 1. find tasknode
240     if (timeout == 0) {
241         XCOLLIE_LOGE("timeout %u invalid, update failed", timeout);
242         return false;
243     }
244     unsigned int seq = WRAP_SEQ(static_cast<unsigned int>(id));
245     if (seq >= MAX_XCOLLIE_NUM) {
246         XCOLLIE_LOGE("update task failed, seq %u limit, id 0x%x valid", seq, id);
247         return false;
248     }
249 
250     // 2. update tasknode
251     std::lock_guard<std::mutex> lock(lock_);
252     struct TaskNode* taskNode = &taskNodes_[seq];
253     if ((taskNode->id != id) || (taskNode->pos == -1)) {
254         XCOLLIE_LOGW("update task failed, already release id 0x%x", id);
255         return false;
256     }
257     taskNode->round = CALC_RING_ROUND(timeout);
258     int pos = CALC_RING_POS(timeout);
259     if (pos == taskNode->pos) {
260         return true;
261     }
262     XCOLLIE_LOGD("TimerRing::UpdateTask ringPos_=%d, pos=%d id =0x%x,name=%s", ringPos_, pos,
263         taskNode->id, taskNode->timerTask.name.c_str());
264     // update ring list
265     ringTaskNodes_[taskNode->pos].remove(taskNode);
266     taskNode->pos = pos;
267     ringTaskNodes_[pos].push_back(taskNode);
268     return true;
269 }
270 } // end of namespace HiviewDFX
271 } // end of namespace OHOS
272