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 name_(std::move(name)),
48 interval_(interval),
49 resetCallback_(resetCallback),
50 expiredCallback_(expiredCallback) {};
51
~HgmOneShotTimer()52 HgmOneShotTimer::~HgmOneShotTimer()
53 {
54 Stop();
55 }
56
Start()57 void HgmOneShotTimer::Start()
58 {
59 int result = sem_init(&semaphone_, 0, 0);
60 HGM_LOGD("HgmOneShotTimer::sem_init result: %{public}d", result);
61 if (!thread_.joinable()) {
62 thread_ = std::thread(&HgmOneShotTimer::Loop, this);
63 }
64 }
65
Stop()66 void HgmOneShotTimer::Stop()
67 {
68 stopFlag_ = true;
69 int result = sem_post(&semaphone_);
70 HGM_LOGD("HgmOneShotTimer::sem_post result: %{public}d", result);
71 if (thread_.joinable()) {
72 thread_.join();
73 result = sem_destroy(&semaphone_);
74 HGM_LOGD("HgmOneShotTimer::sem_destroy result: %{public}d", result);
75 }
76 }
77
Loop()78 void HgmOneShotTimer::Loop()
79 {
80 pthread_setname_np(pthread_self(), name_.c_str());
81 HgmTimerState state = HgmTimerState::RESET;
82 while (true) {
83 bool resetFlag = false;
84 bool expiredFlag = false;
85 state = CheckForResetAndStop(state);
86 if (state == HgmTimerState::STOP) {
87 break;
88 }
89 if (state == HgmTimerState::IDLE) {
90 int result = sem_wait(&semaphone_);
91 if (result && errno != EINTR) {
92 HGM_LOGE("HgmOneShotTimer::sem_wait failed (%{public}s)", std::to_string(errno).c_str());
93 }
94 continue;
95 }
96 if (state == HgmTimerState::RESET) {
97 resetFlag = true;
98 }
99 if (resetFlag && resetCallback_) {
100 resetCallback_();
101 }
102 state = CheckForResetAndStop(state);
103 if (state == HgmTimerState::STOP) {
104 break;
105 }
106 auto expireTime = clock_->Now() + interval_;
107 state = HgmTimerState::WAITING;
108 while (state == HgmTimerState::WAITING) {
109 struct timespec ts;
110 CalculateTimeoutTime(std::chrono::nanoseconds(interval_), &ts);
111 int result = sem_timedwait(&semaphone_, &ts);
112 if (result && errno != ETIMEDOUT && errno != EINTR) {
113 HGM_LOGE("HgmOneShotTimer::sem_timedwait failed (%{public}s)", std::to_string(errno).c_str());
114 }
115 state = CheckForResetAndStop(state);
116 if (state == HgmTimerState::RESET) {
117 expireTime = clock_->Now() + interval_;
118 state = HgmTimerState::WAITING;
119 } else if (state == HgmTimerState::WAITING && CheckTimerExpired(expireTime)) {
120 expiredFlag = true;
121 state = HgmTimerState::IDLE;
122 }
123 }
124 if (expiredFlag && expiredCallback_) {
125 expiredCallback_();
126 }
127 }
128 }
129
CheckTimerExpired(std::chrono::steady_clock::time_point expireTime) const130 bool HgmOneShotTimer::CheckTimerExpired(std::chrono::steady_clock::time_point expireTime) const
131 {
132 return (expireTime - clock_->Now()) <= ZERO;
133 }
134
CheckForResetAndStop(HgmTimerState state)135 HgmOneShotTimer::HgmTimerState HgmOneShotTimer::CheckForResetAndStop(HgmTimerState state)
136 {
137 if (stopFlag_.exchange(false)) {
138 return HgmTimerState::STOP;
139 }
140 if (state != HgmTimerState::STOP && resetFlag_.exchange(false)) {
141 return HgmTimerState::RESET;
142 }
143 return state;
144 }
145
Reset()146 void HgmOneShotTimer::Reset()
147 {
148 resetFlag_ = true;
149 int result = sem_post(&semaphone_);
150 HGM_LOGD("HgmOneShotTimer::sem_post result: %{public}d", result);
151 }
152
Dump() const153 std::string HgmOneShotTimer::Dump() const
154 {
155 std::ostringstream stream;
156 stream << interval_.count() << "ms";
157 return stream.str();
158 }
159 } // namespace OHOS::Rosen