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