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