1 /*
2  * Copyright (c) 2023 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 "hgm_one_shot_timer.h"
17 #include <sstream>
18 #include "hgm_log.h"
19 namespace OHOS::Rosen {
20 namespace {
21 using namespace std::chrono_literals;
22 using nsecs_t =  int64_t;
23 
24 constexpr int64_t NS_TO_SECONDS = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
25 constexpr auto ZERO = std::chrono::steady_clock::duration::zero();
26 
SystemTime()27 static inline nsecs_t SystemTime()
28 {
29     struct timespec t;
30     t.tv_sec = t.tv_nsec = 0;
31     clock_gettime(CLOCK_REALTIME, &t);
32     return nsecs_t(t.tv_sec) * NS_TO_SECONDS + t.tv_nsec;
33 }
34 
CalculateTimeoutTime(std::chrono::nanoseconds timestamp,timespec * spec)35 void CalculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec)
36 {
37     const nsecs_t timeout = SystemTime() + timestamp.count();
38     spec->tv_sec = static_cast<time_t>(timeout / NS_TO_SECONDS);
39     spec->tv_nsec = timeout % NS_TO_SECONDS;
40 }
41 } // namespace
42 
HgmOneShotTimer(std::string name,const Interval & interval,const ResetCallback & resetCallback,const ExpiredCallback & expiredCallback,std::unique_ptr<ChronoSteadyClock> clock)43 HgmOneShotTimer::HgmOneShotTimer(std::string name, const Interval& interval,
44     const ResetCallback& resetCallback, const ExpiredCallback& expiredCallback,
45     std::unique_ptr<ChronoSteadyClock> clock)
46     : clock_(std::move(clock)),
47       runner_(AppExecFwk::EventRunner::Create(name)),
48       name_(std::move(name)),
49       interval_(interval),
50       resetCallback_(resetCallback),
51       expiredCallback_(expiredCallback)
52 {
53     int result = sem_init(&semaphone_, 0, 0);
54     HGM_LOGD("HgmOneShotTimer::sem_init result: %{public}d", result);
55     handler_ = std::make_shared<AppExecFwk::EventHandler>(runner_);
56 };
57 
~HgmOneShotTimer()58 HgmOneShotTimer::~HgmOneShotTimer()
59 {
60     if (handler_ != nullptr) {
61         handler_->RemoveAllEvents();
62     }
63     Stop();
64     int result = sem_destroy(&semaphone_);
65     HGM_LOGD("HgmOneShotTimer::sem_destroy result: %{public}d", result);
66 }
67 
Start()68 void HgmOneShotTimer::Start()
69 {
70     std::lock_guard<std::mutex> lock(startMutex_);
71     if (handler_ != nullptr && handler_->IsIdle()) {
72         handler_->PostTask([this] () { Loop(); });
73     }
74 }
75 
Stop()76 void HgmOneShotTimer::Stop()
77 {
78     if (handler_ == nullptr || handler_->IsIdle()) {
79         return;
80     }
81     stopFlag_.store(true);
82     int result = sem_post(&semaphone_);
83     HGM_LOGD("HgmOneShotTimer::sem_post result: %{public}d", result);
84 }
85 
Loop()86 void HgmOneShotTimer::Loop()
87 {
88     HgmTimerState state = HgmTimerState::RESET;
89     while (true) {
90         bool resetFlag = false;
91         bool expiredFlag = false;
92         state = CheckForResetAndStop(state);
93         if (state == HgmTimerState::STOP) {
94             break;
95         }
96         if (state == HgmTimerState::IDLE) {
97             int result = sem_wait(&semaphone_);
98             if (result && errno != EINTR) {
99                 HGM_LOGE("HgmOneShotTimer::sem_wait failed (%{public}s)", std::to_string(errno).c_str());
100             }
101             continue;
102         }
103         if (state == HgmTimerState::RESET) {
104             resetFlag = true;
105         }
106         if (resetFlag && resetCallback_) {
107             resetCallback_();
108         }
109         state = CheckForResetAndStop(state);
110         if (state == HgmTimerState::STOP) {
111             break;
112         }
113         auto expireTime = clock_->Now() + interval_;
114         state = HgmTimerState::WAITING;
115         while (state == HgmTimerState::WAITING) {
116             struct timespec ts;
117             CalculateTimeoutTime(std::chrono::nanoseconds(interval_), &ts);
118             int result = sem_timedwait(&semaphone_, &ts);
119             if (result && errno != ETIMEDOUT && errno != EINTR) {
120                 HGM_LOGE("HgmOneShotTimer::sem_timedwait failed (%{public}s)", std::to_string(errno).c_str());
121             }
122             state = CheckForResetAndStop(state);
123             if (state == HgmTimerState::RESET) {
124                 expireTime = clock_->Now() + interval_;
125                 state = HgmTimerState::WAITING;
126             } else if (state == HgmTimerState::WAITING && CheckTimerExpired(expireTime)) {
127                 expiredFlag = true;
128                 state = HgmTimerState::IDLE;
129             }
130         }
131         if (expiredFlag && expiredCallback_) {
132             expiredCallback_();
133         }
134     }
135 }
136 
CheckTimerExpired(std::chrono::steady_clock::time_point expireTime) const137 bool HgmOneShotTimer::CheckTimerExpired(std::chrono::steady_clock::time_point expireTime) const
138 {
139     return (expireTime - clock_->Now()) <= ZERO;
140 }
141 
CheckForResetAndStop(HgmTimerState state)142 HgmOneShotTimer::HgmTimerState HgmOneShotTimer::CheckForResetAndStop(HgmTimerState state)
143 {
144     if (stopFlag_.exchange(false)) {
145         return HgmTimerState::STOP;
146     }
147     if (state != HgmTimerState::STOP && resetFlag_.exchange(false)) {
148         return HgmTimerState::RESET;
149     }
150     return state;
151 }
152 
Reset()153 void HgmOneShotTimer::Reset()
154 {
155     resetFlag_ = true;
156     int result = sem_post(&semaphone_);
157     HGM_LOGD("HgmOneShotTimer::sem_post result: %{public}d", result);
158 }
159 
Dump() const160 std::string HgmOneShotTimer::Dump() const
161 {
162     std::ostringstream stream;
163     stream << interval_.count() << "ms";
164     return stream.str();
165 }
166 } // namespace OHOS::Rosen