1 /*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "repeating_timer.h"
18
19 #include <base/functional/callback.h>
20 #include <bluetooth/log.h>
21
22 #include "message_loop_thread.h"
23
24 namespace bluetooth {
25
26 namespace common {
27
28 constexpr std::chrono::microseconds kMinimumPeriod =
29 std::chrono::microseconds(1);
30
31 // This runs on user thread
~RepeatingTimer()32 RepeatingTimer::~RepeatingTimer() {
33 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
34 if (message_loop_thread_ != nullptr && message_loop_thread_->IsRunning()) {
35 CancelAndWait();
36 }
37 }
38
39 // This runs on user thread
SchedulePeriodic(const base::WeakPtr<MessageLoopThread> & thread,const base::Location & from_here,base::RepeatingClosure task,std::chrono::microseconds period)40 bool RepeatingTimer::SchedulePeriodic(
41 const base::WeakPtr<MessageLoopThread>& thread,
42 const base::Location& from_here, base::RepeatingClosure task,
43 std::chrono::microseconds period) {
44 if (period < kMinimumPeriod) {
45 log::error("period must be at least {}", kMinimumPeriod.count());
46 return false;
47 }
48
49 uint64_t time_now_us = clock_tick_us_();
50 uint64_t time_next_task_us = time_now_us + period.count();
51 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
52 if (thread == nullptr) {
53 log::error("thread must be non-null");
54 return false;
55 }
56 CancelAndWait();
57 expected_time_next_task_us_ = time_next_task_us;
58 task_ = std::move(task);
59 task_wrapper_.Reset(
60 base::Bind(&RepeatingTimer::RunTask, base::Unretained(this)));
61 message_loop_thread_ = thread;
62 period_ = period;
63 uint64_t time_until_next_us = time_next_task_us - clock_tick_us_();
64 if (!thread->DoInThreadDelayed(
65 from_here, task_wrapper_.callback(),
66 std::chrono::microseconds(time_until_next_us))) {
67 log::error("failed to post task to message loop for thread {}, from {}",
68 *thread, from_here.ToString());
69 expected_time_next_task_us_ = 0;
70 task_wrapper_.Cancel();
71 message_loop_thread_ = nullptr;
72 period_ = {};
73 return false;
74 }
75 return true;
76 }
77
78 // This runs on user thread
Cancel()79 void RepeatingTimer::Cancel() {
80 std::promise<void> promise;
81 CancelHelper(std::move(promise));
82 }
83
84 // This runs on user thread
CancelAndWait()85 void RepeatingTimer::CancelAndWait() {
86 std::promise<void> promise;
87 auto future = promise.get_future();
88 CancelHelper(std::move(promise));
89 future.wait();
90 }
91
92 // This runs on user thread
CancelHelper(std::promise<void> promise)93 void RepeatingTimer::CancelHelper(std::promise<void> promise) {
94 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
95 MessageLoopThread* scheduled_thread = message_loop_thread_.get();
96 if (scheduled_thread == nullptr) {
97 promise.set_value();
98 return;
99 }
100 if (scheduled_thread->GetThreadId() == base::PlatformThread::CurrentId()) {
101 CancelClosure(std::move(promise));
102 return;
103 }
104 scheduled_thread->DoInThread(
105 FROM_HERE, base::BindOnce(&RepeatingTimer::CancelClosure,
106 base::Unretained(this), std::move(promise)));
107 }
108
109 // This runs on message loop thread
CancelClosure(std::promise<void> promise)110 void RepeatingTimer::CancelClosure(std::promise<void> promise) {
111 message_loop_thread_ = nullptr;
112 task_wrapper_.Cancel();
113 #if BASE_VER < 927031
114 task_ = {};
115 #else
116 task_ = base::NullCallback();
117 #endif
118 period_ = std::chrono::microseconds(0);
119 expected_time_next_task_us_ = 0;
120 promise.set_value();
121 }
122
123 // This runs on user thread
IsScheduled() const124 bool RepeatingTimer::IsScheduled() const {
125 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
126 return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning();
127 }
128
129 // This runs on message loop thread
RunTask()130 void RepeatingTimer::RunTask() {
131 if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) {
132 log::error("message_loop_thread_ is null or is not running");
133 return;
134 }
135 log::assert_that(
136 message_loop_thread_->GetThreadId() == base::PlatformThread::CurrentId(),
137 "task must run on message loop thread");
138
139 int64_t period_us = period_.count();
140 expected_time_next_task_us_ += period_us;
141 uint64_t time_now_us = clock_tick_us_();
142 int64_t remaining_time_us = expected_time_next_task_us_ - time_now_us;
143 if (remaining_time_us < 0) {
144 // if remaining_time_us is negative, schedule the task to the nearest
145 // multiple of period
146 remaining_time_us = (remaining_time_us % period_us + period_us) % period_us;
147 }
148 message_loop_thread_->DoInThreadDelayed(
149 FROM_HERE, task_wrapper_.callback(),
150 std::chrono::microseconds(remaining_time_us));
151
152 uint64_t time_before_task_us = clock_tick_us_();
153 task_.Run();
154 uint64_t time_after_task_us = clock_tick_us_();
155 auto task_time_us =
156 static_cast<int64_t>(time_after_task_us - time_before_task_us);
157 if (task_time_us > period_.count()) {
158 log::error(
159 "Periodic task execution took {} microseconds, longer than interval {} "
160 "microseconds",
161 task_time_us, period_.count());
162 }
163 }
164
165 } // namespace common
166
167 } // namespace bluetooth
168