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 #pragma once
18 
19 #include <base/functional/bind.h>
20 #include <base/location.h>
21 #include <base/run_loop.h>
22 #include <base/threading/platform_thread.h>
23 #include <bluetooth/log.h>
24 #include <unistd.h>
25 
26 #include <future>
27 #include <string>
28 #include <thread>
29 
30 #include "abstract_message_loop.h"
31 #include "common/postable_context.h"
32 
33 namespace bluetooth {
34 
35 namespace common {
36 
37 /**
38  * An interface to various thread related functionality
39  */
40 class MessageLoopThread final : public PostableContext {
41  public:
42   /**
43    * Create a message loop thread with name. Thread won't be running until
44    * StartUp is called.
45    *
46    * @param thread_name name of this worker thread
47    */
48   explicit MessageLoopThread(const std::string& thread_name);
49 
50   MessageLoopThread(const MessageLoopThread&) = delete;
51   MessageLoopThread& operator=(const MessageLoopThread&) = delete;
52 
53   /**
54    * Destroys the message loop thread automatically when it goes out of scope
55    */
56   ~MessageLoopThread();
57 
58   /**
59    * Start the underlying thread. Blocks until all thread infrastructure is
60    * setup. IsRunning() and DoInThread() should return true after this call.
61    * Blocks until the thread is successfully started.
62    *
63    * Repeated call to this method will only start this thread once
64    */
65   void StartUp();
66 
67   /**
68    * Post a task to run on this thread
69    *
70    * @param from_here location where this task is originated
71    * @param task task created through base::Bind()
72    * @return true if task is successfully scheduled, false if task cannot be
73    * scheduled
74    */
75   bool DoInThread(const base::Location& from_here, base::OnceClosure task);
76 
77   /**
78    * Shutdown the current thread as if it is never started. IsRunning() and
79    * DoInThread() will return false after this call. Blocks until the thread is
80    * joined and freed. This thread can be re-started again using StartUp()
81    *
82    * Repeated call to this method will only stop this thread once
83    *
84    * NOTE: Should never be called on the thread itself to avoid deadlock
85    */
86   void ShutDown();
87 
88   /**
89    * Get the current thread ID returned by PlatformThread::CurrentId()
90    *
91    * On Android platform, this value should be the same as the tid logged by
92    * logcat, which is returned by gettid(). On other platform, this thread id
93    * may have different meanings. Therefore, this ID is only good for logging
94    * and thread comparison purpose
95    *
96    * @return this thread's ID
97    */
98   base::PlatformThreadId GetThreadId() const;
99 
100   /**
101    * Get this thread's name set in constructor
102    *
103    * @return this thread's name set in constructor
104    */
105   std::string GetName() const;
106 
107   /**
108    * Get a string representation of this thread
109    *
110    * @return a string representation of this thread
111    */
112   std::string ToString() const;
113 
114   /**
115    * Check if this thread is running
116    *
117    * @return true iff this thread is running and is able to do task
118    */
119   bool IsRunning() const;
120 
121   /**
122    * Attempt to make scheduling for this thread real time
123    *
124    * @return true on success, false otherwise
125    */
126   bool EnableRealTimeScheduling();
127 
128   /**
129    * Return the weak pointer to this object. This can be useful when posting
130    * delayed tasks to this MessageLoopThread using Timer.
131    */
132   base::WeakPtr<MessageLoopThread> GetWeakPtr();
133 
134   /**
135    * Return the message loop for this thread. Accessing raw message loop is not
136    * recommended as message loop can be freed internally.
137    *
138    * @return message loop associated with this thread, nullptr if thread is not
139    * running
140    */
141   btbase::AbstractMessageLoop* message_loop() const;
142 
143   /**
144    * Post a task to run on this thread after a specified delay. If the task
145    * needs to be cancelable before it's run, use base::CancelableClosure type
146    * for task closure. For example:
147    * <code>
148    * base::CancelableClosure cancelable_task;
149    * cancelable_task.Reset(base::Bind(...)); // bind the task
150    * same_thread->DoInThreadDelayed(FROM_HERE,
151    *                                cancelable_task.callback(), delay);
152    * ...
153    * // Cancel the task closure
154    * same_thread->DoInThread(FROM_HERE,
155    *                         base::Bind(&base::CancelableClosure::Cancel,
156    *                                    base::Unretained(&cancelable_task)));
157    * </code>
158    *
159    * Warning: base::CancelableClosure objects must be created on, posted to,
160    * cancelled on, and destroyed on the same thread.
161    *
162    * @param from_here location where this task is originated
163    * @param task task created through base::Bind()
164    * @param delay delay for the task to be executed
165    * @return true if task is successfully scheduled, false if task cannot be
166    * scheduled
167    */
168   bool DoInThreadDelayed(const base::Location& from_here,
169                          base::OnceClosure task,
170                          std::chrono::microseconds delay);
171   /**
172    * Wrapper around DoInThread without a location.
173    */
174   void Post(base::OnceClosure closure) override;
175 
176   /**
177    * Returns a postable object
178    */
179   PostableContext* Postable();
180 
181  private:
182   /**
183    * Static method to run the thread
184    *
185    * This is used instead of a C++ lambda because of the use of std::shared_ptr
186    *
187    * @param context needs to be a pointer to an instance of MessageLoopThread
188    * @param start_up_promise a std::promise that is used to notify calling
189    * thread the completion of message loop start-up
190    */
191   static void RunThread(MessageLoopThread* context,
192                         std::promise<void> start_up_promise);
193 
194   /**
195    * Actual method to run the thread, blocking until ShutDown() is called
196    *
197    * @param start_up_promise a std::promise that is used to notify calling
198    * thread the completion of message loop start-up
199    */
200   void Run(std::promise<void> start_up_promise);
201 
202   mutable std::recursive_mutex api_mutex_;
203   const std::string thread_name_;
204   btbase::AbstractMessageLoop* message_loop_;
205   base::RunLoop* run_loop_;
206   std::thread* thread_;
207   base::PlatformThreadId thread_id_;
208   // Linux specific abstractions
209   pid_t linux_tid_;
210   base::WeakPtrFactory<MessageLoopThread> weak_ptr_factory_;
211   bool shutting_down_;
212 };
213 
214 inline std::ostream& operator<<(std::ostream& os,
215                                 const bluetooth::common::MessageLoopThread& a) {
216   os << a.ToString();
217   return os;
218 }
219 
220 }  // namespace common
221 }  // namespace bluetooth
222 
223 namespace fmt {
224 template <>
225 struct formatter<bluetooth::common::MessageLoopThread> : ostream_formatter {};
226 }  // namespace fmt
227