1020a203aSopenharmony_ci/*
2020a203aSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
3020a203aSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4020a203aSopenharmony_ci * you may not use this file except in compliance with the License.
5020a203aSopenharmony_ci * You may obtain a copy of the License at
6020a203aSopenharmony_ci *
7020a203aSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8020a203aSopenharmony_ci *
9020a203aSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10020a203aSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11020a203aSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12020a203aSopenharmony_ci * See the License for the specific language governing permissions and
13020a203aSopenharmony_ci * limitations under the License.
14020a203aSopenharmony_ci */
15020a203aSopenharmony_ci
16020a203aSopenharmony_ci#include "SceneTimerOhImpl.h"
17020a203aSopenharmony_ci#include <sys/prctl.h>
18020a203aSopenharmony_ci#include <chrono>
19020a203aSopenharmony_ci#include <thread>
20020a203aSopenharmony_ci#include "hiview_logger.h"
21020a203aSopenharmony_ci
22020a203aSopenharmony_cinamespace OHOS {
23020a203aSopenharmony_cinamespace HiviewDFX {
24020a203aSopenharmony_ciDEFINE_LOG_LABEL(0xD002D66, "Hiview-XPerformance");
25020a203aSopenharmony_ci
26020a203aSopenharmony_ciusing namespace std::chrono;
27020a203aSopenharmony_ci
28020a203aSopenharmony_ciconst static std::string TIMER_THREAD_NAME = "XperfTimerThr";
29020a203aSopenharmony_ci
30020a203aSopenharmony_ciSceneTimerOhImpl::SceneTimerOhImpl()
31020a203aSopenharmony_ci{
32020a203aSopenharmony_ci    std::thread timerThread(&SceneTimerOhImpl::Loop, this);
33020a203aSopenharmony_ci    timerThread.detach();
34020a203aSopenharmony_ci}
35020a203aSopenharmony_ci
36020a203aSopenharmony_civoid SceneTimerOhImpl::RegUser(int userId, ICb* cb)
37020a203aSopenharmony_ci{
38020a203aSopenharmony_ci    callbacks[userId] = cb;
39020a203aSopenharmony_ci}
40020a203aSopenharmony_ci
41020a203aSopenharmony_civoid SceneTimerOhImpl::UnRegUser(int userId)
42020a203aSopenharmony_ci{
43020a203aSopenharmony_ci    callbacks.erase(userId);
44020a203aSopenharmony_ci}
45020a203aSopenharmony_ci
46020a203aSopenharmony_civoid SceneTimerOhImpl::Start(int user, int id, long interval)
47020a203aSopenharmony_ci{
48020a203aSopenharmony_ci    int key = BuildRecordKey(user, id);
49020a203aSopenharmony_ci    ValidateDuplication(key);
50020a203aSopenharmony_ci    long long expireTs = CalcExpireTimeStamp(interval);
51020a203aSopenharmony_ci    FillRecordAndNotify(key, expireTs);
52020a203aSopenharmony_ci}
53020a203aSopenharmony_ci
54020a203aSopenharmony_civoid SceneTimerOhImpl::Stop(int user, int id)
55020a203aSopenharmony_ci{
56020a203aSopenharmony_ci    int key = BuildRecordKey(user, id);
57020a203aSopenharmony_ci    ValidateExistence(key);
58020a203aSopenharmony_ci    RemoveRecordAndNotify(key);
59020a203aSopenharmony_ci}
60020a203aSopenharmony_ci
61020a203aSopenharmony_civoid SceneTimerOhImpl::Loop()
62020a203aSopenharmony_ci{
63020a203aSopenharmony_ci    std::unique_lock<std::mutex> uniqueLock(mut);
64020a203aSopenharmony_ci    prctl(PR_SET_NAME, TIMER_THREAD_NAME.c_str(), nullptr, nullptr, nullptr);
65020a203aSopenharmony_ci    milliseconds interval(checkInterval);
66020a203aSopenharmony_ci    while (true) {
67020a203aSopenharmony_ci        if (!records.empty()) {
68020a203aSopenharmony_ci            cv.wait_for(uniqueLock, interval);
69020a203aSopenharmony_ci            /* note the lock is held now */
70020a203aSopenharmony_ci            CheckRecordsAndTrigger();
71020a203aSopenharmony_ci            continue;
72020a203aSopenharmony_ci        }
73020a203aSopenharmony_ci        cv.wait(uniqueLock);
74020a203aSopenharmony_ci    }
75020a203aSopenharmony_ci}
76020a203aSopenharmony_ci
77020a203aSopenharmony_civoid SceneTimerOhImpl::FillRecordAndNotify(int key, long long expireTs)
78020a203aSopenharmony_ci{
79020a203aSopenharmony_ci    std::unique_lock<std::mutex> uniqueLock(mut);
80020a203aSopenharmony_ci    // I believe this double-check is needed
81020a203aSopenharmony_ci    if (records.find(key) != records.end()) {
82020a203aSopenharmony_ci        throw std::invalid_argument("duplicated id");
83020a203aSopenharmony_ci    }
84020a203aSopenharmony_ci    records.emplace(key, expireTs);
85020a203aSopenharmony_ci    cv.notify_all(); // remember this
86020a203aSopenharmony_ci}
87020a203aSopenharmony_ci
88020a203aSopenharmony_civoid SceneTimerOhImpl::ValidateDuplication(int key)
89020a203aSopenharmony_ci{
90020a203aSopenharmony_ci    std::unique_lock<std::mutex> uniqueLock(mut);
91020a203aSopenharmony_ci    if (records.find(key) != records.end()) {
92020a203aSopenharmony_ci        throw std::invalid_argument("duplicated id");
93020a203aSopenharmony_ci    }
94020a203aSopenharmony_ci}
95020a203aSopenharmony_ci
96020a203aSopenharmony_civoid SceneTimerOhImpl::ValidateExistence(int key)
97020a203aSopenharmony_ci{
98020a203aSopenharmony_ci    std::unique_lock<std::mutex> uniqueLock(mut);
99020a203aSopenharmony_ci    if (records.find(key) == records.end()) {
100020a203aSopenharmony_ci        throw std::invalid_argument("non-existing id");
101020a203aSopenharmony_ci    }
102020a203aSopenharmony_ci}
103020a203aSopenharmony_ci
104020a203aSopenharmony_cilong long SceneTimerOhImpl::GetCurTimeStamp()
105020a203aSopenharmony_ci{
106020a203aSopenharmony_ci    milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
107020a203aSopenharmony_ci    return ms.count();
108020a203aSopenharmony_ci}
109020a203aSopenharmony_ci
110020a203aSopenharmony_ci/* already have lock */
111020a203aSopenharmony_civoid SceneTimerOhImpl::CheckRecordsAndTrigger()
112020a203aSopenharmony_ci{
113020a203aSopenharmony_ci    long long nowMs = GetCurTimeStamp();
114020a203aSopenharmony_ci    for (std::map<int, long long>::iterator it = records.begin(); it != records.end();) {
115020a203aSopenharmony_ci        long long expire = it->second;
116020a203aSopenharmony_ci        int key = it->first;
117020a203aSopenharmony_ci        if (nowMs >= expire) {
118020a203aSopenharmony_ci            it = records.erase(it);
119020a203aSopenharmony_ci            TriggerCallbak(key);
120020a203aSopenharmony_ci        } else {
121020a203aSopenharmony_ci            it++;
122020a203aSopenharmony_ci        }
123020a203aSopenharmony_ci    }
124020a203aSopenharmony_ci}
125020a203aSopenharmony_ci
126020a203aSopenharmony_ciint SceneTimerOhImpl::BuildRecordKey(int user, int id)
127020a203aSopenharmony_ci{
128020a203aSopenharmony_ci    if (user >= maxUserId) {
129020a203aSopenharmony_ci        throw std::invalid_argument("exceeds max user id");
130020a203aSopenharmony_ci    }
131020a203aSopenharmony_ci    if (id >= maxRecordPerUser) {
132020a203aSopenharmony_ci        throw std::invalid_argument("exceeds max id per user");
133020a203aSopenharmony_ci    }
134020a203aSopenharmony_ci    int ret = user * maxRecordPerUser + id;
135020a203aSopenharmony_ci    return ret;
136020a203aSopenharmony_ci}
137020a203aSopenharmony_ci
138020a203aSopenharmony_cilong long SceneTimerOhImpl::CalcExpireTimeStamp(long delay)
139020a203aSopenharmony_ci{
140020a203aSopenharmony_ci    long long nowMs = GetCurTimeStamp();
141020a203aSopenharmony_ci    long long expire = nowMs + delay;
142020a203aSopenharmony_ci    return expire;
143020a203aSopenharmony_ci}
144020a203aSopenharmony_ci
145020a203aSopenharmony_civoid SceneTimerOhImpl::TriggerCallbak(int recordKey)
146020a203aSopenharmony_ci{
147020a203aSopenharmony_ci    int user = ExtractUserFromRecordKey(recordKey);
148020a203aSopenharmony_ci    int id = ExtractIdFromRecordKey(recordKey);
149020a203aSopenharmony_ci    try {
150020a203aSopenharmony_ci        ICb* cb = callbacks.at(user);
151020a203aSopenharmony_ci        if (cb == nullptr) {
152020a203aSopenharmony_ci            HIVIEW_LOGE("SceneTimerImpl::TriggerCallbak cb is null");
153020a203aSopenharmony_ci            return;
154020a203aSopenharmony_ci        }
155020a203aSopenharmony_ci        cb->Expired(id);
156020a203aSopenharmony_ci    }
157020a203aSopenharmony_ci    catch (const std::out_of_range& outex) {
158020a203aSopenharmony_ci        HIVIEW_LOGE("SceneTimerImpl::back id=%{public}d;user=%{public}d;key=%{public}d;", id, user, recordKey);
159020a203aSopenharmony_ci    }
160020a203aSopenharmony_ci}
161020a203aSopenharmony_ci
162020a203aSopenharmony_ciint SceneTimerOhImpl::ExtractUserFromRecordKey(int key)
163020a203aSopenharmony_ci{
164020a203aSopenharmony_ci    return key / maxRecordPerUser;
165020a203aSopenharmony_ci}
166020a203aSopenharmony_ci
167020a203aSopenharmony_ciint SceneTimerOhImpl::ExtractIdFromRecordKey(int key)
168020a203aSopenharmony_ci{
169020a203aSopenharmony_ci    return key % maxRecordPerUser;
170020a203aSopenharmony_ci}
171020a203aSopenharmony_ci
172020a203aSopenharmony_civoid SceneTimerOhImpl::RemoveRecordAndNotify(int key)
173020a203aSopenharmony_ci{
174020a203aSopenharmony_ci    std::unique_lock<std::mutex> uniqueLock(mut);
175020a203aSopenharmony_ci    records.erase(records.find(key));
176020a203aSopenharmony_ci    cv.notify_all();
177020a203aSopenharmony_ci}
178020a203aSopenharmony_ci} // HiviewDFX
179020a203aSopenharmony_ci} // OHOS