| /* |
| * Copyright (C) 2019 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 "perfetto/base/build_config.h" |
| |
| #include "perfetto/ext/base/thread_task_runner.h" |
| |
| #include <condition_variable> |
| #include <functional> |
| #include <mutex> |
| #include <thread> |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/thread_utils.h" |
| #include "perfetto/ext/base/unix_task_runner.h" |
| |
| #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ |
| PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
| #include <sys/prctl.h> |
| #endif |
| |
| namespace perfetto { |
| namespace base { |
| |
| ThreadTaskRunner::ThreadTaskRunner(ThreadTaskRunner&& other) noexcept |
| : thread_(std::move(other.thread_)), task_runner_(other.task_runner_) { |
| other.task_runner_ = nullptr; |
| } |
| |
| ThreadTaskRunner& ThreadTaskRunner::operator=(ThreadTaskRunner&& other) { |
| this->~ThreadTaskRunner(); |
| new (this) ThreadTaskRunner(std::move(other)); |
| return *this; |
| } |
| |
| ThreadTaskRunner::~ThreadTaskRunner() { |
| if (task_runner_) { |
| PERFETTO_CHECK(!task_runner_->QuitCalled()); |
| task_runner_->Quit(); |
| |
| PERFETTO_DCHECK(thread_.joinable()); |
| } |
| if (thread_.joinable()) |
| thread_.join(); |
| } |
| |
| ThreadTaskRunner::ThreadTaskRunner(const std::string& name) : name_(name) { |
| std::mutex init_lock; |
| std::condition_variable init_cv; |
| |
| std::function<void(UnixTaskRunner*)> initializer = |
| [this, &init_lock, &init_cv](UnixTaskRunner* task_runner) { |
| std::lock_guard<std::mutex> lock(init_lock); |
| task_runner_ = task_runner; |
| // Notify while still holding the lock, as init_cv ceases to exist as |
| // soon as the main thread observes a non-null task_runner_, and it can |
| // wake up spuriously (i.e. before the notify if we had unlocked before |
| // notifying). |
| init_cv.notify_one(); |
| }; |
| |
| thread_ = std::thread(&ThreadTaskRunner::RunTaskThread, this, |
| std::move(initializer)); |
| |
| std::unique_lock<std::mutex> lock(init_lock); |
| init_cv.wait(lock, [this] { return !!task_runner_; }); |
| } |
| |
| void ThreadTaskRunner::RunTaskThread( |
| std::function<void(UnixTaskRunner*)> initializer) { |
| if (!name_.empty()) { |
| base::MaybeSetThreadName(name_); |
| } |
| |
| UnixTaskRunner task_runner; |
| task_runner.PostTask(std::bind(std::move(initializer), &task_runner)); |
| task_runner.Run(); |
| } |
| |
| void ThreadTaskRunner::PostTaskAndWaitForTesting(std::function<void()> fn) { |
| std::mutex mutex; |
| std::condition_variable cv; |
| |
| std::unique_lock<std::mutex> lock(mutex); |
| bool done = false; |
| task_runner_->PostTask([&mutex, &cv, &done, &fn] { |
| fn(); |
| |
| std::lock_guard<std::mutex> inner_lock(mutex); |
| done = true; |
| cv.notify_one(); |
| }); |
| cv.wait(lock, [&done] { return done; }); |
| } |
| |
| uint64_t ThreadTaskRunner::GetThreadCPUTimeNsForTesting() { |
| uint64_t thread_time_ns = 0; |
| PostTaskAndWaitForTesting([&thread_time_ns] { |
| thread_time_ns = static_cast<uint64_t>(base::GetThreadCPUTimeNs().count()); |
| }); |
| return thread_time_ns; |
| } |
| |
| void ThreadTaskRunner::PostTask(std::function<void()> task) { |
| task_runner_->PostTask(std::move(task)); |
| } |
| |
| void ThreadTaskRunner::PostDelayedTask(std::function<void()> task, |
| uint32_t delay_ms) { |
| task_runner_->PostDelayedTask(std::move(task), delay_ms); |
| } |
| |
| void ThreadTaskRunner::AddFileDescriptorWatch( |
| PlatformHandle handle, |
| std::function<void()> watch_task) { |
| task_runner_->AddFileDescriptorWatch(handle, std::move(watch_task)); |
| } |
| |
| void ThreadTaskRunner::RemoveFileDescriptorWatch(PlatformHandle handle) { |
| task_runner_->RemoveFileDescriptorWatch(handle); |
| } |
| |
| bool ThreadTaskRunner::RunsTasksOnCurrentThread() const { |
| return task_runner_->RunsTasksOnCurrentThread(); |
| } |
| |
| } // namespace base |
| } // namespace perfetto |