• 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 namespace bluetooth {
23 
24 namespace common {
25 
26 constexpr base::TimeDelta kMinimumPeriod = base::TimeDelta::FromMicroseconds(1);
27 
28 // This runs on user thread
~RepeatingTimer()29 RepeatingTimer::~RepeatingTimer() {
30   std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
31   if (message_loop_thread_ != nullptr && message_loop_thread_->IsRunning()) {
32     CancelAndWait();
33   }
34 }
35 
36 // This runs on user thread
SchedulePeriodic(const base::WeakPtr<MessageLoopThread> & thread,const base::Location & from_here,base::Closure task,base::TimeDelta period)37 bool RepeatingTimer::SchedulePeriodic(
38     const base::WeakPtr<MessageLoopThread>& thread,
39     const base::Location& from_here, base::Closure task,
40     base::TimeDelta period) {
41   if (period < kMinimumPeriod) {
42     LOG(ERROR) << __func__ << ": period must be at least " << kMinimumPeriod;
43     return false;
44   }
45 
46   uint64_t time_now_us = time_get_os_boottime_us();
47   uint64_t time_next_task_us = time_now_us + period.InMicroseconds();
48   std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
49   if (thread == nullptr) {
50     LOG(ERROR) << __func__ << ": thread must be non-null";
51     return false;
52   }
53   CancelAndWait();
54   expected_time_next_task_us_ = time_next_task_us;
55   task_ = std::move(task);
56   task_wrapper_.Reset(
57       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 - time_get_os_boottime_us();
61   if (!thread->DoInThreadDelayed(
62           from_here, task_wrapper_.callback(),
63           base::TimeDelta::FromMicroseconds(time_until_next_us))) {
64     LOG(ERROR) << __func__
65                << ": failed to post task to message loop for thread " << *thread
66                << ", from " << from_here.ToString();
67     expected_time_next_task_us_ = 0;
68     task_wrapper_.Cancel();
69     message_loop_thread_ = nullptr;
70     period_ = {};
71     return false;
72   }
73   return true;
74 }
75 
76 // This runs on user thread
Cancel()77 void RepeatingTimer::Cancel() {
78   std::promise<void> promise;
79   CancelHelper(std::move(promise));
80 }
81 
82 // This runs on user thread
CancelAndWait()83 void RepeatingTimer::CancelAndWait() {
84   std::promise<void> promise;
85   auto future = promise.get_future();
86   CancelHelper(std::move(promise));
87   future.wait();
88 }
89 
90 // This runs on user thread
CancelHelper(std::promise<void> promise)91 void RepeatingTimer::CancelHelper(std::promise<void> promise) {
92   std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
93   MessageLoopThread* scheduled_thread = message_loop_thread_.get();
94   if (scheduled_thread == nullptr) {
95     promise.set_value();
96     return;
97   }
98   if (scheduled_thread->GetThreadId() == base::PlatformThread::CurrentId()) {
99     CancelClosure(std::move(promise));
100     return;
101   }
102   scheduled_thread->DoInThread(
103       FROM_HERE, base::BindOnce(&RepeatingTimer::CancelClosure,
104                                 base::Unretained(this), std::move(promise)));
105 }
106 
107 // This runs on message loop thread
CancelClosure(std::promise<void> promise)108 void RepeatingTimer::CancelClosure(std::promise<void> promise) {
109   message_loop_thread_ = nullptr;
110   task_wrapper_.Cancel();
111   task_ = {};
112   period_ = base::TimeDelta();
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) << __func__
127                << ": message_loop_thread_ is null or is not running";
128     return;
129   }
130   CHECK_EQ(message_loop_thread_->GetThreadId(),
131            base::PlatformThread::CurrentId())
132       << ": task must run on message loop thread";
133 
134   int64_t period_us = period_.InMicroseconds();
135   expected_time_next_task_us_ += period_us;
136   uint64_t time_now_us = time_get_os_boottime_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(
144       FROM_HERE, task_wrapper_.callback(),
145       base::TimeDelta::FromMicroseconds(remaining_time_us));
146 
147   uint64_t time_before_task_us = time_get_os_boottime_us();
148   task_.Run();
149   uint64_t time_after_task_us = time_get_os_boottime_us();
150   auto task_time_us =
151       static_cast<int64_t>(time_after_task_us - time_before_task_us);
152   if (task_time_us > period_.InMicroseconds()) {
153     LOG(ERROR) << __func__ << ": Periodic task execution took " << task_time_us
154                << " microseconds, longer than interval "
155                << period_.InMicroseconds() << " microseconds";
156   }
157 }
158 
159 }  // namespace common
160 
161 }  // namespace bluetooth
162