• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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