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