blob: f0037d1287f629e520a8b1856be3560aab835ea7 [file] [log] [blame]
Hansong Zhang438b08c2018-08-14 14:29:23 -07001/*
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 "timer.h"
18#include "message_loop_thread.h"
19#include "time_util.h"
20
21namespace bluetooth {
22
23namespace common {
24
25constexpr base::TimeDelta kMinimumPeriod = base::TimeDelta::FromMicroseconds(1);
26
Hansong Zhang68f91132018-09-20 10:15:12 -070027// This runs on user thread
Hansong Zhang438b08c2018-08-14 14:29:23 -070028Timer::~Timer() {
29 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
30 if (message_loop_thread_ != nullptr && message_loop_thread_->IsRunning()) {
31 CancelAndWait();
32 }
33}
34
35// This runs on user thread
36bool Timer::Schedule(const base::WeakPtr<MessageLoopThread>& thread,
Jakub Pawlowski67f5f372018-07-23 10:00:25 -070037 const base::Location& from_here, base::Closure task,
38 base::TimeDelta delay) {
Hansong Zhang438b08c2018-08-14 14:29:23 -070039 return ScheduleTaskHelper(thread, from_here, std::move(task), delay, false);
40}
41
42// This runs on user thread
43bool Timer::SchedulePeriodic(const base::WeakPtr<MessageLoopThread>& thread,
Jakub Pawlowski67f5f372018-07-23 10:00:25 -070044 const base::Location& from_here,
Hansong Zhang438b08c2018-08-14 14:29:23 -070045 base::Closure task, base::TimeDelta period) {
46 if (period < kMinimumPeriod) {
47 LOG(ERROR) << __func__ << ": period must be at least " << kMinimumPeriod;
48 return false;
49 }
50 return ScheduleTaskHelper(thread, from_here, std::move(task), period, true);
51}
52
53// This runs on user thread
54bool Timer::ScheduleTaskHelper(const base::WeakPtr<MessageLoopThread>& thread,
Jakub Pawlowski67f5f372018-07-23 10:00:25 -070055 const base::Location& from_here,
Hansong Zhang438b08c2018-08-14 14:29:23 -070056 base::Closure task, base::TimeDelta delay,
57 bool is_periodic) {
58 uint64_t time_now_us = time_get_os_boottime_us();
59 uint64_t time_next_task_us = time_now_us + delay.InMicroseconds();
60 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
61 if (thread == nullptr) {
62 LOG(ERROR) << __func__ << ": thread must be non-null";
63 return false;
64 }
65 CancelAndWait();
66 expected_time_next_task_us_ = time_next_task_us;
67 task_ = std::move(task);
Hansong Zhang85ea7632018-09-20 10:15:12 -070068 task_wrapper_.Reset(base::Bind(&Timer::RunTask, base::Unretained(this)));
Hansong Zhang1773dd82018-09-28 12:10:15 -070069 message_loop_thread_ = thread;
70 period_ = delay;
71 is_periodic_ = is_periodic;
Hansong Zhang438b08c2018-08-14 14:29:23 -070072 uint64_t time_until_next_us = time_next_task_us - time_get_os_boottime_us();
73 if (!thread->DoInThreadDelayed(
Hansong Zhang85ea7632018-09-20 10:15:12 -070074 from_here, task_wrapper_.callback(),
Hansong Zhang438b08c2018-08-14 14:29:23 -070075 base::TimeDelta::FromMicroseconds(time_until_next_us))) {
76 LOG(ERROR) << __func__
77 << ": failed to post task to message loop for thread " << *thread
78 << ", from " << from_here.ToString();
79 expected_time_next_task_us_ = 0;
Hansong Zhang85ea7632018-09-20 10:15:12 -070080 task_wrapper_.Cancel();
Hansong Zhang1773dd82018-09-28 12:10:15 -070081 message_loop_thread_ = nullptr;
82 period_ = {};
83 is_periodic_ = false;
Hansong Zhang438b08c2018-08-14 14:29:23 -070084 return false;
85 }
Hansong Zhang438b08c2018-08-14 14:29:23 -070086 return true;
87}
88
89// This runs on user thread
90void Timer::Cancel() {
Hansong Zhang68f91132018-09-20 10:15:12 -070091 std::promise<void> promise;
92 CancelHelper(std::move(promise));
Hansong Zhang438b08c2018-08-14 14:29:23 -070093}
94
95// This runs on user thread
96void Timer::CancelAndWait() {
Hansong Zhang68f91132018-09-20 10:15:12 -070097 std::promise<void> promise;
98 auto future = promise.get_future();
99 CancelHelper(std::move(promise));
100 future.wait();
Hansong Zhang438b08c2018-08-14 14:29:23 -0700101}
102
103// This runs on user thread
Hansong Zhang68f91132018-09-20 10:15:12 -0700104void Timer::CancelHelper(std::promise<void> promise) {
105 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
Hansong Zhang85ea7632018-09-20 10:15:12 -0700106 MessageLoopThread* scheduled_thread = message_loop_thread_.get();
107 if (scheduled_thread == nullptr) {
Hansong Zhang68f91132018-09-20 10:15:12 -0700108 promise.set_value();
Hansong Zhang438b08c2018-08-14 14:29:23 -0700109 return;
110 }
Hansong Zhang85ea7632018-09-20 10:15:12 -0700111 if (scheduled_thread->GetThreadId() == base::PlatformThread::CurrentId()) {
Hansong Zhang438b08c2018-08-14 14:29:23 -0700112 CancelClosure(std::move(promise));
113 return;
114 }
Hansong Zhang85ea7632018-09-20 10:15:12 -0700115 scheduled_thread->DoInThread(
Hansong Zhang438b08c2018-08-14 14:29:23 -0700116 FROM_HERE, base::BindOnce(&Timer::CancelClosure, base::Unretained(this),
117 std::move(promise)));
Hansong Zhang438b08c2018-08-14 14:29:23 -0700118}
119
120// This runs on message loop thread
121void Timer::CancelClosure(std::promise<void> promise) {
122 message_loop_thread_ = nullptr;
Hansong Zhang85ea7632018-09-20 10:15:12 -0700123 task_wrapper_.Cancel();
124 task_ = {};
Hansong Zhang438b08c2018-08-14 14:29:23 -0700125 period_ = base::TimeDelta();
126 is_periodic_ = false;
127 expected_time_next_task_us_ = 0;
128 promise.set_value();
129}
130
Hansong Zhang68f91132018-09-20 10:15:12 -0700131// This runs on user thread
Hansong Zhang438b08c2018-08-14 14:29:23 -0700132bool Timer::IsScheduled() const {
133 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
134 return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning();
135}
136
Hansong Zhang68f91132018-09-20 10:15:12 -0700137// This runs on message loop thread
Hansong Zhang438b08c2018-08-14 14:29:23 -0700138void Timer::RunTask() {
139 if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) {
140 LOG(ERROR) << __func__
141 << ": message_loop_thread_ is null or is not running";
142 return;
143 }
Hansong Zhang68f91132018-09-20 10:15:12 -0700144 CHECK_EQ(message_loop_thread_->GetThreadId(),
145 base::PlatformThread::CurrentId())
146 << ": task must run on message loop thread";
Hansong Zhang438b08c2018-08-14 14:29:23 -0700147 if (is_periodic_) {
Hansong Zhang68f91132018-09-20 10:15:12 -0700148 RunPeriodicTask();
149 } else {
150 RunSingleTask();
Hansong Zhang438b08c2018-08-14 14:29:23 -0700151 }
Hansong Zhang68f91132018-09-20 10:15:12 -0700152}
153
154// This runs on message loop thread
155void Timer::RunPeriodicTask() {
156 int64_t period_us = period_.InMicroseconds();
157 expected_time_next_task_us_ += period_us;
158 uint64_t time_now_us = time_get_os_boottime_us();
159 int64_t remaining_time_us = expected_time_next_task_us_ - time_now_us;
160 if (remaining_time_us < 0) {
161 // if remaining_time_us is negative, schedule the task to the nearest
162 // multiple of period
163 remaining_time_us = (remaining_time_us % period_us + period_us) % period_us;
164 }
165 message_loop_thread_->DoInThreadDelayed(
Hansong Zhang85ea7632018-09-20 10:15:12 -0700166 FROM_HERE, task_wrapper_.callback(),
Hansong Zhang68f91132018-09-20 10:15:12 -0700167 base::TimeDelta::FromMicroseconds(remaining_time_us));
168
Hansong Zhang438b08c2018-08-14 14:29:23 -0700169 uint64_t time_before_task_us = time_get_os_boottime_us();
170 task_.Run();
171 uint64_t time_after_task_us = time_get_os_boottime_us();
Hansong Zhang68f91132018-09-20 10:15:12 -0700172 auto task_time_us =
Hansong Zhang438b08c2018-08-14 14:29:23 -0700173 static_cast<int64_t>(time_after_task_us - time_before_task_us);
Hansong Zhang68f91132018-09-20 10:15:12 -0700174 if (task_time_us > period_.InMicroseconds()) {
Hansong Zhang438b08c2018-08-14 14:29:23 -0700175 LOG(ERROR) << __func__ << ": Periodic task execution took " << task_time_us
176 << " microseconds, longer than interval "
177 << period_.InMicroseconds() << " microseconds";
178 }
Hansong Zhang68f91132018-09-20 10:15:12 -0700179}
180
181// This runs on message loop thread
182void Timer::RunSingleTask() {
Hansong Zhang85ea7632018-09-20 10:15:12 -0700183 base::OnceClosure current_task = std::move(task_);
184 task_wrapper_.Cancel();
185 task_ = {};
Hansong Zhang68f91132018-09-20 10:15:12 -0700186 period_ = base::TimeDelta();
187 expected_time_next_task_us_ = 0;
Hansong Zhang85ea7632018-09-20 10:15:12 -0700188 std::move(current_task).Run();
189 message_loop_thread_ = nullptr;
Hansong Zhang438b08c2018-08-14 14:29:23 -0700190}
191
192} // namespace common
193
194} // namespace bluetooth