1 /*
2 * Copyright (c) 2023 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 #include "timer_mgr.h"
16
17 #include <unistd.h>
18 #include <sys/time.h>
19 #include <sstream>
20 #include "intell_voice_log.h"
21
22 #define LOG_TAG "TimerMgr"
23
24 using namespace std;
25
26 namespace OHOS {
27 namespace IntellVoiceUtils {
LogTime(const string & prefix)28 static void LogTime(const string& prefix)
29 {
30 struct timeval now;
31 if (gettimeofday(&now, nullptr) < 0) {
32 INTELL_VOICE_LOG_ERROR("gettimeoftoday time info error");
33 return;
34 }
35
36 struct tm *nowtm = localtime(&now.tv_sec);
37 if (nowtm == nullptr) {
38 INTELL_VOICE_LOG_ERROR("nowtm is nullptr");
39 return;
40 }
41
42 char tmbuf[64] = {0};
43 if (strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", nowtm)) {
44 INTELL_VOICE_LOG_INFO("%s %s.%lld", prefix.c_str(), tmbuf, static_cast<long long>(now.tv_usec));
45 }
46 }
47
NowTimeUs()48 static int64_t NowTimeUs()
49 {
50 struct timespec t;
51 if (clock_gettime(CLOCK_MONOTONIC, &t) < 0) {
52 INTELL_VOICE_LOG_ERROR("clock gettime failed");
53 return 0;
54 }
55 return t.tv_sec * 1000000LL + t.tv_nsec / 1000LL;
56 }
57
TimerItem(int id,int type,int cookie,int64_t delayUs,ITimerObserver * observer)58 TimerItem::TimerItem(int id, int type, int cookie, int64_t delayUs, ITimerObserver* observer)
59 : timerId(id), type(type), cookie(cookie), observer(observer)
60 {
61 tgtUs = NowTimeUs() + delayUs;
62 }
63
TimerMgr(int maxTimerNum)64 TimerMgr::TimerMgr(int maxTimerNum) : IdAllocator(maxTimerNum), status_(TimerStatus::TIMER_STATUS_INIT),
65 timerObserver_(nullptr)
66 {
67 }
68
~TimerMgr()69 TimerMgr::~TimerMgr()
70 {
71 Stop();
72 }
73
Start(ITimerObserver * observer)74 void TimerMgr::Start(ITimerObserver* observer)
75 {
76 unique_lock<mutex> lock(timeMutex_);
77
78 if (status_ == TimerStatus::TIMER_STATUS_STARTED) {
79 return;
80 }
81
82 if (status_ != TimerStatus::TIMER_STATUS_INIT) {
83 INTELL_VOICE_LOG_ERROR("status is not init");
84 return;
85 }
86
87 workThread_ = thread(&TimerMgr::WorkThread, this);
88
89 timerObserver_ = observer;
90 status_ = TimerStatus::TIMER_STATUS_STARTED;
91 LogTime("start timermgr");
92 }
93
Stop()94 void TimerMgr::Stop()
95 {
96 {
97 lock_guard<mutex> lock(timeMutex_);
98 if (status_ != TimerStatus::TIMER_STATUS_STARTED) {
99 INTELL_VOICE_LOG_ERROR("status is not started");
100 return;
101 }
102 status_ = TimerStatus::TIMER_STATUS_TO_QUIT;
103 LogTime("stop timermgr");
104 cv_.notify_all();
105 }
106
107 if (workThread_.joinable()) {
108 workThread_.join();
109 }
110
111 Clear();
112 }
113
SetTimer(int type,int64_t delayUs,int cookie,ITimerObserver * currObserver)114 int TimerMgr::SetTimer(int type, int64_t delayUs, int cookie, ITimerObserver* currObserver)
115 {
116 unique_lock<mutex> lock(timeMutex_);
117
118 if (status_ != TimerStatus::TIMER_STATUS_STARTED) {
119 INTELL_VOICE_LOG_ERROR("timer mgr not started");
120 return INVALID_ID;
121 }
122
123 ITimerObserver* observer = currObserver == nullptr ? timerObserver_ : currObserver;
124 if (observer == nullptr) {
125 INTELL_VOICE_LOG_ERROR("observer is null");
126 return INVALID_ID;
127 }
128
129 int id = AllocId();
130 if (id == INVALID_ID) {
131 INTELL_VOICE_LOG_ERROR("no available id");
132 return INVALID_ID;
133 }
134
135 shared_ptr<TimerItem> addItem = make_shared<TimerItem>(id, type, cookie, delayUs, observer);
136 if (addItem == nullptr) {
137 INTELL_VOICE_LOG_ERROR("no avaiable memory");
138 ReleaseId(id);
139 return INVALID_ID;
140 }
141
142 auto it = itemQueue_.begin();
143 while (it != itemQueue_.end() && (*it)->tgtUs < addItem->tgtUs) {
144 ++it;
145 }
146
147 bool needNotify = (it == itemQueue_.begin());
148 itemQueue_.insert(it, addItem);
149
150 if (needNotify) {
151 cv_.notify_one();
152 }
153
154 ostringstream oss;
155 oss << "set timer id " << id << " type " << type << " delay " << delayUs << " cookie " << cookie << " time ";
156 LogTime(oss.str());
157
158 return id;
159 }
160
ResetTimer(int timerId,int type,int64_t delayUs,int cookie,ITimerObserver * currObserver)161 int TimerMgr::ResetTimer(int timerId, int type, int64_t delayUs, int cookie, ITimerObserver* currObserver)
162 {
163 {
164 unique_lock<mutex> lock(timeMutex_);
165 if (itemQueue_.size() == 1) {
166 auto it = itemQueue_.begin();
167 if ((*it)->timerId != timerId) {
168 INTELL_VOICE_LOG_ERROR("id %d can not correspond with timerId %d", (*it)->timerId, timerId);
169 return INVALID_ID;
170 }
171 (*it)->tgtUs = NowTimeUs() + delayUs;
172 cv_.notify_one();
173 return timerId;
174 }
175 }
176 KillTimer(timerId);
177 return SetTimer(type, delayUs, cookie, currObserver);
178 }
179
KillTimer(int & timerId)180 void TimerMgr::KillTimer(int& timerId)
181 {
182 unique_lock<mutex> lock(timeMutex_);
183 INTELL_VOICE_LOG_INFO("kill timer %d", timerId);
184 for (auto it = itemQueue_.begin(); it != itemQueue_.end(); it++) {
185 shared_ptr<TimerItem> curItem = *it;
186 if (curItem->timerId == timerId) {
187 INTELL_VOICE_LOG_INFO("kill timer id %d type %d path %d", timerId, curItem->type, curItem->cookie);
188
189 ReleaseId(curItem->timerId);
190 itemQueue_.erase(it);
191 timerId = INVALID_ID;
192 break;
193 }
194 }
195 }
196
Clear()197 void TimerMgr::Clear()
198 {
199 lock_guard<mutex> lock(timeMutex_);
200
201 itemQueue_.clear();
202 IdAllocator::ClearId();
203
204 status_ = TimerStatus::TIMER_STATUS_INIT;
205 timerObserver_ = nullptr;
206 }
207
WorkThread()208 void TimerMgr::WorkThread()
209 {
210 while (true) {
211 TimerItem item;
212 {
213 unique_lock<mutex> lock(timeMutex_);
214
215 if (status_ != TimerStatus::TIMER_STATUS_STARTED) {
216 break;
217 }
218
219 if (itemQueue_.empty()) {
220 cv_.wait(lock);
221 continue;
222 }
223
224 item = *itemQueue_.front();
225 int64_t now = NowTimeUs();
226 if (now < item.tgtUs) {
227 cv_.wait_for(lock, chrono::microseconds(item.tgtUs - now));
228 continue;
229 }
230 ReleaseId(item.timerId);
231 itemQueue_.pop_front();
232 }
233
234 if (item.observer != nullptr) {
235 TimerEvent info(item.type, item.timerId, item.cookie);
236 item.observer->OnTimerEvent(info);
237 }
238 };
239
240 INTELL_VOICE_LOG_INFO("timer thread exit");
241 }
242 }
243 }
244