/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "message_loop_thread.h" #include #include #include #include namespace bluetooth { namespace common { static constexpr int kRealTimeFifoSchedulingPriority = 1; MessageLoopThread::MessageLoopThread(const std::string& thread_name) : thread_name_(thread_name), message_loop_(nullptr), run_loop_(nullptr), thread_(nullptr), thread_id_(-1), linux_tid_(-1), weak_ptr_factory_(this), shutting_down_(false) {} MessageLoopThread::~MessageLoopThread() { ShutDown(); } void MessageLoopThread::StartUp() { std::promise start_up_promise; std::future start_up_future = start_up_promise.get_future(); { std::lock_guard api_lock(api_mutex_); if (thread_ != nullptr) { LOG(WARNING) << __func__ << ": thread " << *this << " is already started"; return; } thread_ = new std::thread(&MessageLoopThread::RunThread, this, std::move(start_up_promise)); } start_up_future.wait(); } bool MessageLoopThread::DoInThread(const base::Location& from_here, base::OnceClosure task) { return DoInThreadDelayed(from_here, std::move(task), base::TimeDelta()); } bool MessageLoopThread::DoInThreadDelayed(const base::Location& from_here, base::OnceClosure task, const base::TimeDelta& delay) { std::lock_guard api_lock(api_mutex_); if (message_loop_ == nullptr) { LOG(ERROR) << __func__ << ": message loop is null for thread " << *this << ", from " << from_here.ToString(); return false; } if (!message_loop_->task_runner()->PostDelayedTask(from_here, std::move(task), delay)) { LOG(ERROR) << __func__ << ": failed to post task to message loop for thread " << *this << ", from " << from_here.ToString(); return false; } return true; } void MessageLoopThread::ShutDown() { { std::lock_guard api_lock(api_mutex_); if (thread_ == nullptr) { LOG(INFO) << __func__ << ": thread " << *this << " is already stopped"; return; } if (message_loop_ == nullptr) { LOG(INFO) << __func__ << ": message_loop_ is null. Already stopping"; return; } if (shutting_down_) { LOG(INFO) << __func__ << ": waiting for thread to join"; return; } shutting_down_ = true; CHECK_NE(thread_id_, base::PlatformThread::CurrentId()) << __func__ << " should not be called on the thread itself. " << "Otherwise, deadlock may happen."; if (!message_loop_->task_runner()->PostTask( FROM_HERE, run_loop_->QuitWhenIdleClosure())) { LOG(FATAL) << __func__ << ": failed to post task to message loop for thread " << *this; } } thread_->join(); { std::lock_guard api_lock(api_mutex_); delete thread_; thread_ = nullptr; shutting_down_ = false; } } base::PlatformThreadId MessageLoopThread::GetThreadId() const { std::lock_guard api_lock(api_mutex_); return thread_id_; } std::string MessageLoopThread::GetName() const { return thread_name_; } std::string MessageLoopThread::ToString() const { std::lock_guard api_lock(api_mutex_); return base::StringPrintf("%s(%d)", thread_name_.c_str(), thread_id_); } bool MessageLoopThread::IsRunning() const { std::lock_guard api_lock(api_mutex_); return message_loop_ != nullptr; } // Non API method, should not be protected by API mutex void MessageLoopThread::RunThread(MessageLoopThread* thread, std::promise start_up_promise) { thread->Run(std::move(start_up_promise)); } base::MessageLoop* MessageLoopThread::message_loop() const { std::lock_guard api_lock(api_mutex_); return message_loop_; } bool MessageLoopThread::EnableRealTimeScheduling() { std::lock_guard api_lock(api_mutex_); if (!IsRunning()) { LOG(ERROR) << __func__ << ": thread " << *this << " is not running"; return false; } struct sched_param rt_params = {.sched_priority = kRealTimeFifoSchedulingPriority}; int rc = sched_setscheduler(linux_tid_, SCHED_FIFO, &rt_params); if (rc != 0) { LOG(ERROR) << __func__ << ": unable to set SCHED_FIFO priority " << kRealTimeFifoSchedulingPriority << " for linux_tid " << std::to_string(linux_tid_) << ", thread " << *this << ", error: " << strerror(errno); return false; } return true; } base::WeakPtr MessageLoopThread::GetWeakPtr() { std::lock_guard api_lock(api_mutex_); return weak_ptr_factory_.GetWeakPtr(); } void MessageLoopThread::Run(std::promise start_up_promise) { { std::lock_guard api_lock(api_mutex_); LOG(INFO) << __func__ << ": message loop starting for thread " << thread_name_; base::PlatformThread::SetName(thread_name_); message_loop_ = new base::MessageLoop(); run_loop_ = new base::RunLoop(); thread_id_ = base::PlatformThread::CurrentId(); linux_tid_ = static_cast(syscall(SYS_gettid)); start_up_promise.set_value(); } // Blocking until ShutDown() is called run_loop_->Run(); { std::lock_guard api_lock(api_mutex_); thread_id_ = -1; linux_tid_ = -1; delete message_loop_; message_loop_ = nullptr; delete run_loop_; run_loop_ = nullptr; LOG(INFO) << __func__ << ": message loop finished for thread " << thread_name_; } } } // namespace common } // namespace bluetooth