1 /*
2 * Copyright (c) 2024 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 "SceneTimerOhImpl.h"
17 #include <sys/prctl.h>
18 #include <chrono>
19 #include <thread>
20 #include "hiview_logger.h"
21
22 namespace OHOS {
23 namespace HiviewDFX {
24 DEFINE_LOG_LABEL(0xD002D66, "Hiview-XPerformance");
25
26 using namespace std::chrono;
27
28 const static std::string TIMER_THREAD_NAME = "XperfTimerThr";
29
SceneTimerOhImpl()30 SceneTimerOhImpl::SceneTimerOhImpl()
31 {
32 std::thread timerThread(&SceneTimerOhImpl::Loop, this);
33 timerThread.detach();
34 }
35
RegUser(int userId, ICb* cb)36 void SceneTimerOhImpl::RegUser(int userId, ICb* cb)
37 {
38 callbacks[userId] = cb;
39 }
40
UnRegUser(int userId)41 void SceneTimerOhImpl::UnRegUser(int userId)
42 {
43 callbacks.erase(userId);
44 }
45
Start(int user, int id, long interval)46 void SceneTimerOhImpl::Start(int user, int id, long interval)
47 {
48 int key = BuildRecordKey(user, id);
49 ValidateDuplication(key);
50 long long expireTs = CalcExpireTimeStamp(interval);
51 FillRecordAndNotify(key, expireTs);
52 }
53
Stop(int user, int id)54 void SceneTimerOhImpl::Stop(int user, int id)
55 {
56 int key = BuildRecordKey(user, id);
57 ValidateExistence(key);
58 RemoveRecordAndNotify(key);
59 }
60
Loop()61 void SceneTimerOhImpl::Loop()
62 {
63 std::unique_lock<std::mutex> uniqueLock(mut);
64 prctl(PR_SET_NAME, TIMER_THREAD_NAME.c_str(), nullptr, nullptr, nullptr);
65 milliseconds interval(checkInterval);
66 while (true) {
67 if (!records.empty()) {
68 cv.wait_for(uniqueLock, interval);
69 /* note the lock is held now */
70 CheckRecordsAndTrigger();
71 continue;
72 }
73 cv.wait(uniqueLock);
74 }
75 }
76
FillRecordAndNotify(int key, long long expireTs)77 void SceneTimerOhImpl::FillRecordAndNotify(int key, long long expireTs)
78 {
79 std::unique_lock<std::mutex> uniqueLock(mut);
80 // I believe this double-check is needed
81 if (records.find(key) != records.end()) {
82 throw std::invalid_argument("duplicated id");
83 }
84 records.emplace(key, expireTs);
85 cv.notify_all(); // remember this
86 }
87
ValidateDuplication(int key)88 void SceneTimerOhImpl::ValidateDuplication(int key)
89 {
90 std::unique_lock<std::mutex> uniqueLock(mut);
91 if (records.find(key) != records.end()) {
92 throw std::invalid_argument("duplicated id");
93 }
94 }
95
ValidateExistence(int key)96 void SceneTimerOhImpl::ValidateExistence(int key)
97 {
98 std::unique_lock<std::mutex> uniqueLock(mut);
99 if (records.find(key) == records.end()) {
100 throw std::invalid_argument("non-existing id");
101 }
102 }
103
GetCurTimeStamp()104 long long SceneTimerOhImpl::GetCurTimeStamp()
105 {
106 milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
107 return ms.count();
108 }
109
110 /* already have lock */
CheckRecordsAndTrigger()111 void SceneTimerOhImpl::CheckRecordsAndTrigger()
112 {
113 long long nowMs = GetCurTimeStamp();
114 for (std::map<int, long long>::iterator it = records.begin(); it != records.end();) {
115 long long expire = it->second;
116 int key = it->first;
117 if (nowMs >= expire) {
118 it = records.erase(it);
119 TriggerCallbak(key);
120 } else {
121 it++;
122 }
123 }
124 }
125
BuildRecordKey(int user, int id)126 int SceneTimerOhImpl::BuildRecordKey(int user, int id)
127 {
128 if (user >= maxUserId) {
129 throw std::invalid_argument("exceeds max user id");
130 }
131 if (id >= maxRecordPerUser) {
132 throw std::invalid_argument("exceeds max id per user");
133 }
134 int ret = user * maxRecordPerUser + id;
135 return ret;
136 }
137
CalcExpireTimeStamp(long delay)138 long long SceneTimerOhImpl::CalcExpireTimeStamp(long delay)
139 {
140 long long nowMs = GetCurTimeStamp();
141 long long expire = nowMs + delay;
142 return expire;
143 }
144
TriggerCallbak(int recordKey)145 void SceneTimerOhImpl::TriggerCallbak(int recordKey)
146 {
147 int user = ExtractUserFromRecordKey(recordKey);
148 int id = ExtractIdFromRecordKey(recordKey);
149 try {
150 ICb* cb = callbacks.at(user);
151 if (cb == nullptr) {
152 HIVIEW_LOGE("SceneTimerImpl::TriggerCallbak cb is null");
153 return;
154 }
155 cb->Expired(id);
156 }
157 catch (const std::out_of_range& outex) {
158 HIVIEW_LOGE("SceneTimerImpl::back id=%{public}d;user=%{public}d;key=%{public}d;", id, user, recordKey);
159 }
160 }
161
ExtractUserFromRecordKey(int key)162 int SceneTimerOhImpl::ExtractUserFromRecordKey(int key)
163 {
164 return key / maxRecordPerUser;
165 }
166
ExtractIdFromRecordKey(int key)167 int SceneTimerOhImpl::ExtractIdFromRecordKey(int key)
168 {
169 return key % maxRecordPerUser;
170 }
171
RemoveRecordAndNotify(int key)172 void SceneTimerOhImpl::RemoveRecordAndNotify(int key)
173 {
174 std::unique_lock<std::mutex> uniqueLock(mut);
175 records.erase(records.find(key));
176 cv.notify_all();
177 }
178 } // HiviewDFX
179 } // OHOS