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 = std::chrono::microseconds(1);
29
30 // This runs on user thread
~RepeatingTimer()31 RepeatingTimer::~RepeatingTimer() {
32 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
33 if (message_loop_thread_ != nullptr && message_loop_thread_->IsRunning()) {
34 CancelAndWait();
35 }
36 }
37
38 // This runs on user thread
SchedulePeriodic(const base::WeakPtr<MessageLoopThread> & thread,base::RepeatingClosure task,std::chrono::microseconds period)39 bool RepeatingTimer::SchedulePeriodic(const base::WeakPtr<MessageLoopThread>& thread,
40 base::RepeatingClosure task,
41 std::chrono::microseconds period) {
42 if (period < kMinimumPeriod) {
43 log::error("period must be at least {}", kMinimumPeriod.count());
44 return false;
45 }
46
47 uint64_t time_now_us = clock_tick_us_();
48 uint64_t time_next_task_us = time_now_us + period.count();
49 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
50 if (thread == nullptr) {
51 log::error("thread must be non-null");
52 return false;
53 }
54 CancelAndWait();
55 expected_time_next_task_us_ = time_next_task_us;
56 task_ = std::move(task);
57 task_wrapper_.Reset(base::Bind(&RepeatingTimer::RunTask, base::Unretained(this)));
58 message_loop_thread_ = thread;
59 period_ = period;
60 uint64_t time_until_next_us = time_next_task_us - clock_tick_us_();
61 if (!thread->DoInThreadDelayed(task_wrapper_.callback(),
62 std::chrono::microseconds(time_until_next_us))) {
63 log::error("failed to post task to message loop for thread {}", *thread);
64 expected_time_next_task_us_ = 0;
65 task_wrapper_.Cancel();
66 message_loop_thread_ = nullptr;
67 period_ = {};
68 return false;
69 }
70 return true;
71 }
72
73 // This runs on user thread
Cancel()74 void RepeatingTimer::Cancel() {
75 std::promise<void> promise;
76 CancelHelper(std::move(promise));
77 }
78
79 // This runs on user thread
CancelAndWait()80 void RepeatingTimer::CancelAndWait() {
81 std::promise<void> promise;
82 auto future = promise.get_future();
83 CancelHelper(std::move(promise));
84 future.wait();
85 }
86
87 // This runs on user thread
CancelHelper(std::promise<void> promise)88 void RepeatingTimer::CancelHelper(std::promise<void> promise) {
89 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
90 MessageLoopThread* scheduled_thread = message_loop_thread_.get();
91 if (scheduled_thread == nullptr) {
92 promise.set_value();
93 return;
94 }
95 if (scheduled_thread->GetThreadId() == base::PlatformThread::CurrentId()) {
96 CancelClosure(std::move(promise));
97 return;
98 }
99 scheduled_thread->DoInThread(base::BindOnce(&RepeatingTimer::CancelClosure,
100 base::Unretained(this), std::move(promise)));
101 }
102
103 // This runs on message loop thread
CancelClosure(std::promise<void> promise)104 void RepeatingTimer::CancelClosure(std::promise<void> promise) {
105 message_loop_thread_ = nullptr;
106 task_wrapper_.Cancel();
107 #if BASE_VER < 927031
108 task_ = {};
109 #else
110 task_ = base::NullCallback();
111 #endif
112 period_ = std::chrono::microseconds(0);
113 expected_time_next_task_us_ = 0;
114 promise.set_value();
115 }
116
117 // This runs on user thread
IsScheduled() const118 bool RepeatingTimer::IsScheduled() const {
119 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
120 return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning();
121 }
122
123 // This runs on message loop thread
RunTask()124 void RepeatingTimer::RunTask() {
125 if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) {
126 log::error("message_loop_thread_ is null or is not running");
127 return;
128 }
129 log::assert_that(message_loop_thread_->GetThreadId() == base::PlatformThread::CurrentId(),
130 "task must run on message loop thread");
131
132 int64_t period_us = period_.count();
133 expected_time_next_task_us_ += period_us;
134 uint64_t time_now_us = clock_tick_us_();
135 int64_t remaining_time_us = expected_time_next_task_us_ - time_now_us;
136 if (remaining_time_us < 0) {
137 // if remaining_time_us is negative, schedule the task to the nearest
138 // multiple of period
139 remaining_time_us = (remaining_time_us % period_us + period_us) % period_us;
140 }
141 message_loop_thread_->DoInThreadDelayed(task_wrapper_.callback(),
142 std::chrono::microseconds(remaining_time_us));
143
144 uint64_t time_before_task_us = clock_tick_us_();
145 task_.Run();
146 uint64_t time_after_task_us = clock_tick_us_();
147 auto task_time_us = static_cast<int64_t>(time_after_task_us - time_before_task_us);
148 if (task_time_us > period_.count()) {
149 log::error("Periodic task execution took {} microseconds, longer than interval {} microseconds",
150 task_time_us, period_.count());
151 }
152 }
153
154 } // namespace common
155
156 } // namespace bluetooth
157