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