1 /*
2 * Copyright (C) 2019 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 "perfetto/base/thread_task_runner.h"
18
19 #include <condition_variable>
20 #include <functional>
21 #include <mutex>
22 #include <thread>
23
24 #include "perfetto/base/logging.h"
25 #include "perfetto/base/unix_task_runner.h"
26
27 namespace perfetto {
28 namespace base {
29
ThreadTaskRunner(ThreadTaskRunner && other)30 ThreadTaskRunner::ThreadTaskRunner(ThreadTaskRunner&& other) noexcept
31 : thread_(std::move(other.thread_)), task_runner_(other.task_runner_) {
32 other.task_runner_ = nullptr;
33 }
34
operator =(ThreadTaskRunner && other)35 ThreadTaskRunner& ThreadTaskRunner::operator=(ThreadTaskRunner&& other) {
36 this->~ThreadTaskRunner();
37 new (this) ThreadTaskRunner(std::move(other));
38 return *this;
39 }
40
~ThreadTaskRunner()41 ThreadTaskRunner::~ThreadTaskRunner() {
42 if (task_runner_) {
43 PERFETTO_CHECK(!task_runner_->QuitCalled());
44 task_runner_->Quit();
45
46 PERFETTO_DCHECK(thread_.joinable());
47 }
48 if (thread_.joinable())
49 thread_.join();
50 }
51
ThreadTaskRunner()52 ThreadTaskRunner::ThreadTaskRunner() {
53 std::mutex init_lock;
54 std::condition_variable init_cv;
55
56 std::function<void(UnixTaskRunner*)> initializer =
57 [this, &init_lock, &init_cv](UnixTaskRunner* task_runner) {
58 std::lock_guard<std::mutex> lock(init_lock);
59 task_runner_ = task_runner;
60 // Notify while still holding the lock, as init_cv ceases to exist as
61 // soon as the main thread observes a non-null task_runner_, and it can
62 // wake up spuriously (i.e. before the notify if we had unlocked before
63 // notifying).
64 init_cv.notify_one();
65 };
66 thread_ = std::thread(&ThreadTaskRunner::RunTaskThread, this,
67 std::move(initializer));
68
69 std::unique_lock<std::mutex> lock(init_lock);
70 init_cv.wait(lock, [this] { return !!task_runner_; });
71 }
72
RunTaskThread(std::function<void (UnixTaskRunner *)> initializer)73 void ThreadTaskRunner::RunTaskThread(
74 std::function<void(UnixTaskRunner*)> initializer) {
75 UnixTaskRunner task_runner;
76 task_runner.PostTask(std::bind(std::move(initializer), &task_runner));
77 task_runner.Run();
78 }
79
80 } // namespace base
81 } // namespace perfetto
82