• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "message_loop_thread.h"
20 #include "time_util.h"
21 
22 #include <base/callback.h>
23 #include <base/logging.h>
24 
25 namespace bluetooth {
26 
27 namespace common {
28 
29 #if BASE_VER < 931007
30 constexpr base::TimeDelta kMinimumPeriod = base::TimeDelta::FromMicroseconds(1);
31 #else
32 constexpr base::TimeDelta kMinimumPeriod = base::Microseconds(1);
33 #endif
34 
35 // This runs on user thread
~RepeatingTimer()36 RepeatingTimer::~RepeatingTimer() {
37   std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
38   if (message_loop_thread_ != nullptr && message_loop_thread_->IsRunning()) {
39     CancelAndWait();
40   }
41 }
42 
43 // This runs on user thread
SchedulePeriodic(const base::WeakPtr<MessageLoopThread> & thread,const base::Location & from_here,base::Closure task,base::TimeDelta period)44 bool RepeatingTimer::SchedulePeriodic(
45     const base::WeakPtr<MessageLoopThread>& thread,
46     const base::Location& from_here, base::Closure task,
47     base::TimeDelta period) {
48   if (period < kMinimumPeriod) {
49     LOG(ERROR) << __func__ << ": period must be at least " << kMinimumPeriod;
50     return false;
51   }
52 
53   uint64_t time_now_us = time_get_os_boottime_us();
54   uint64_t time_next_task_us = time_now_us + period.InMicroseconds();
55   std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
56   if (thread == nullptr) {
57     LOG(ERROR) << __func__ << ": thread must be non-null";
58     return false;
59   }
60   CancelAndWait();
61   expected_time_next_task_us_ = time_next_task_us;
62   task_ = std::move(task);
63   task_wrapper_.Reset(
64       base::Bind(&RepeatingTimer::RunTask, base::Unretained(this)));
65   message_loop_thread_ = thread;
66   period_ = period;
67   uint64_t time_until_next_us = time_next_task_us - time_get_os_boottime_us();
68   if (!thread->DoInThreadDelayed(
69           from_here, task_wrapper_.callback(),
70 #if BASE_VER < 931007
71           base::TimeDelta::FromMicroseconds(time_until_next_us))) {
72 #else
73           base::Microseconds(time_until_next_us))) {
74 #endif
75     LOG(ERROR) << __func__
76                << ": failed to post task to message loop for thread " << *thread
77                << ", from " << from_here.ToString();
78     expected_time_next_task_us_ = 0;
79     task_wrapper_.Cancel();
80     message_loop_thread_ = nullptr;
81     period_ = {};
82     return false;
83   }
84   return true;
85 }
86 
87 // This runs on user thread
88 void RepeatingTimer::Cancel() {
89   std::promise<void> promise;
90   CancelHelper(std::move(promise));
91 }
92 
93 // This runs on user thread
94 void RepeatingTimer::CancelAndWait() {
95   std::promise<void> promise;
96   auto future = promise.get_future();
97   CancelHelper(std::move(promise));
98   future.wait();
99 }
100 
101 // This runs on user thread
102 void RepeatingTimer::CancelHelper(std::promise<void> promise) {
103   std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
104   MessageLoopThread* scheduled_thread = message_loop_thread_.get();
105   if (scheduled_thread == nullptr) {
106     promise.set_value();
107     return;
108   }
109   if (scheduled_thread->GetThreadId() == base::PlatformThread::CurrentId()) {
110     CancelClosure(std::move(promise));
111     return;
112   }
113   scheduled_thread->DoInThread(
114       FROM_HERE, base::BindOnce(&RepeatingTimer::CancelClosure,
115                                 base::Unretained(this), std::move(promise)));
116 }
117 
118 // This runs on message loop thread
119 void RepeatingTimer::CancelClosure(std::promise<void> promise) {
120   message_loop_thread_ = nullptr;
121   task_wrapper_.Cancel();
122 #if BASE_VER < 927031
123   task_ = {};
124 #else
125   task_ = base::NullCallback();
126 #endif
127   period_ = base::TimeDelta();
128   expected_time_next_task_us_ = 0;
129   promise.set_value();
130 }
131 
132 // This runs on user thread
133 bool RepeatingTimer::IsScheduled() const {
134   std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
135   return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning();
136 }
137 
138 // This runs on message loop thread
139 void RepeatingTimer::RunTask() {
140   if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) {
141     LOG(ERROR) << __func__
142                << ": message_loop_thread_ is null or is not running";
143     return;
144   }
145   CHECK_EQ(message_loop_thread_->GetThreadId(),
146            base::PlatformThread::CurrentId())
147       << ": task must run on message loop thread";
148 
149   int64_t period_us = period_.InMicroseconds();
150   expected_time_next_task_us_ += period_us;
151   uint64_t time_now_us = time_get_os_boottime_us();
152   int64_t remaining_time_us = expected_time_next_task_us_ - time_now_us;
153   if (remaining_time_us < 0) {
154     // if remaining_time_us is negative, schedule the task to the nearest
155     // multiple of period
156     remaining_time_us = (remaining_time_us % period_us + period_us) % period_us;
157   }
158   message_loop_thread_->DoInThreadDelayed(
159       FROM_HERE, task_wrapper_.callback(),
160 #if BASE_VER < 931007
161       base::TimeDelta::FromMicroseconds(remaining_time_us));
162 #else
163       base::Microseconds(remaining_time_us));
164 #endif
165 
166   uint64_t time_before_task_us = time_get_os_boottime_us();
167   task_.Run();
168   uint64_t time_after_task_us = time_get_os_boottime_us();
169   auto task_time_us =
170       static_cast<int64_t>(time_after_task_us - time_before_task_us);
171   if (task_time_us > period_.InMicroseconds()) {
172     LOG(ERROR) << __func__ << ": Periodic task execution took " << task_time_us
173                << " microseconds, longer than interval "
174                << period_.InMicroseconds() << " microseconds";
175   }
176 }
177 
178 }  // namespace common
179 
180 }  // namespace bluetooth
181