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 "once_timer.h"
18
19 #include "message_loop_thread.h"
20 #include "time_util.h"
21
22 #include <base/logging.h>
23
24 namespace bluetooth {
25
26 namespace common {
27
28 // This runs on user thread
~OnceTimer()29 OnceTimer::~OnceTimer() {
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
Schedule(const base::WeakPtr<MessageLoopThread> & thread,const base::Location & from_here,base::OnceClosure task,base::TimeDelta delay)37 bool OnceTimer::Schedule(const base::WeakPtr<MessageLoopThread>& thread,
38 const base::Location& from_here,
39 base::OnceClosure task, base::TimeDelta delay) {
40 uint64_t time_now_us = time_get_os_boottime_us();
41 uint64_t time_next_task_us = time_now_us + delay.InMicroseconds();
42 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
43 if (thread == nullptr) {
44 LOG(ERROR) << __func__ << ": thread must be non-null";
45 return false;
46 }
47 CancelAndWait();
48 task_ = std::move(task);
49 task_wrapper_.Reset(
50 base::BindOnce(&OnceTimer::RunTask, base::Unretained(this)));
51 message_loop_thread_ = thread;
52 delay_ = delay;
53 uint64_t time_until_next_us = time_next_task_us - time_get_os_boottime_us();
54 if (!thread->DoInThreadDelayed(
55 from_here, task_wrapper_.callback(),
56 #if BASE_VER < 931007
57 base::TimeDelta::FromMicroseconds(time_until_next_us))) {
58 #else
59 base::Microseconds(time_until_next_us))) {
60 #endif
61 LOG(ERROR) << __func__
62 << ": failed to post task to message loop for thread " << *thread
63 << ", from " << from_here.ToString();
64 task_wrapper_.Cancel();
65 message_loop_thread_ = nullptr;
66 delay_ = {};
67 return false;
68 }
69 return true;
70 }
71
72 // This runs on user thread
73 void OnceTimer::Cancel() {
74 std::promise<void> promise;
75 CancelHelper(std::move(promise));
76 }
77
78 // This runs on user thread
79 void OnceTimer::CancelAndWait() {
80 std::promise<void> promise;
81 auto future = promise.get_future();
82 CancelHelper(std::move(promise));
83 future.wait();
84 }
85
86 // This runs on user thread
87 void OnceTimer::CancelHelper(std::promise<void> promise) {
88 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
89 MessageLoopThread* scheduled_thread = message_loop_thread_.get();
90 if (scheduled_thread == nullptr) {
91 promise.set_value();
92 return;
93 }
94 if (scheduled_thread->GetThreadId() == base::PlatformThread::CurrentId()) {
95 CancelClosure(std::move(promise));
96 return;
97 }
98 scheduled_thread->DoInThread(
99 FROM_HERE, base::BindOnce(&OnceTimer::CancelClosure,
100 base::Unretained(this), std::move(promise)));
101 }
102
103 // This runs on message loop thread
104 void OnceTimer::CancelClosure(std::promise<void> promise) {
105 message_loop_thread_ = nullptr;
106 task_wrapper_.Cancel();
107 std::move(task_);
108 delay_ = base::TimeDelta();
109 promise.set_value();
110 }
111
112 // This runs on user thread
113 bool OnceTimer::IsScheduled() const {
114 std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
115 return message_loop_thread_ != nullptr && message_loop_thread_->IsRunning();
116 }
117
118 // This runs on message loop thread
119 void OnceTimer::RunTask() {
120 if (message_loop_thread_ == nullptr || !message_loop_thread_->IsRunning()) {
121 LOG(ERROR) << __func__
122 << ": message_loop_thread_ is null or is not running";
123 return;
124 }
125 CHECK_EQ(message_loop_thread_->GetThreadId(),
126 base::PlatformThread::CurrentId())
127 << ": task must run on message loop thread";
128
129 task_wrapper_.Cancel();
130 std::move(task_).Run();
131 message_loop_thread_ = nullptr;
132 }
133
134 } // namespace common
135
136 } // namespace bluetooth
137