123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/tracing/perfetto_task_runner.h"
- #include <memory>
- #include <utility>
- #include <vector>
- #include "base/files/scoped_file.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/weak_ptr.h"
- #include "base/task/thread_pool.h"
- #include "base/test/bind.h"
- #include "base/test/task_environment.h"
- #include "base/threading/simple_thread.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)
- #include <sys/socket.h>
- #include <sys/types.h>
- #include "base/posix/eintr_wrapper.h"
- #endif
- namespace base {
- namespace tracing {
- namespace {
- class TaskDestination {
- public:
- TaskDestination(size_t number_of_sequences,
- size_t expected_tasks,
- base::OnceClosure on_complete)
- : expected_tasks_(expected_tasks),
- on_complete_(std::move(on_complete)),
- last_task_id_(number_of_sequences) {}
- size_t tasks_run() const { return tasks_run_; }
- void TestTask(int n, size_t sequence_number = 0) {
- EXPECT_LT(sequence_number, last_task_id_.size());
- EXPECT_GT(expected_tasks_, tasks_run_);
- EXPECT_GE(n, last_task_id_[sequence_number]);
- last_task_id_[sequence_number] = n;
- if (++tasks_run_ == expected_tasks_) {
- std::move(on_complete_).Run();
- }
- }
- base::WeakPtr<TaskDestination> GetWeakPtr() {
- return weak_ptr_factory_.GetWeakPtr();
- }
- private:
- const size_t expected_tasks_;
- base::OnceClosure on_complete_;
- std::vector<int> last_task_id_;
- size_t tasks_run_ = 0;
- base::WeakPtrFactory<TaskDestination> weak_ptr_factory_{this};
- };
- class PosterThread : public base::SimpleThread {
- public:
- PosterThread(PerfettoTaskRunner* task_runner,
- base::WeakPtr<TaskDestination> weak_ptr,
- int n,
- size_t sequence_number)
- : SimpleThread("TaskPostThread"),
- task_runner_(task_runner),
- weak_ptr_(weak_ptr),
- n_(n),
- sequence_number_(sequence_number) {}
- ~PosterThread() override {}
- // base::SimpleThread overrides.
- void BeforeStart() override {}
- void BeforeJoin() override {}
- void Run() override {
- for (int i = 0; i < n_; ++i) {
- auto weak_ptr = weak_ptr_;
- auto sequence_number = sequence_number_;
- task_runner_->PostTask([weak_ptr, i, sequence_number]() {
- weak_ptr->TestTask(i, sequence_number);
- });
- }
- }
- private:
- raw_ptr<PerfettoTaskRunner> task_runner_;
- base::WeakPtr<TaskDestination> weak_ptr_;
- const int n_;
- const size_t sequence_number_;
- };
- class PerfettoTaskRunnerTest : public testing::Test {
- public:
- void SetUp() override {
- sequenced_task_runner_ = CreateNewTaskrunner();
- task_runner_ = std::make_unique<PerfettoTaskRunner>(sequenced_task_runner_);
- }
- scoped_refptr<base::SequencedTaskRunner> CreateNewTaskrunner() {
- return base::ThreadPool::CreateSingleThreadTaskRunner(
- {base::MayBlock()}, base::SingleThreadTaskRunnerThreadMode::DEDICATED);
- }
- void SetTaskExpectations(base::OnceClosure on_complete,
- size_t expected_tasks,
- size_t number_of_sequences = 1) {
- task_destination_ = std::make_unique<TaskDestination>(
- number_of_sequences, expected_tasks, std::move(on_complete));
- }
- void TearDown() override {
- sequenced_task_runner_->DeleteSoon(FROM_HERE, std::move(task_runner_));
- }
- PerfettoTaskRunner* task_runner() { return task_runner_.get(); }
- TaskDestination* destination() { return task_destination_.get(); }
- base::test::TaskEnvironment& task_environment() { return task_environment_; }
- private:
- base::test::TaskEnvironment task_environment_;
- scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
- std::unique_ptr<PerfettoTaskRunner> task_runner_;
- std::unique_ptr<TaskDestination> task_destination_;
- };
- TEST_F(PerfettoTaskRunnerTest, SequentialTasks) {
- base::RunLoop wait_for_tasks;
- SetTaskExpectations(wait_for_tasks.QuitClosure(), 3);
- auto weak_ptr = destination()->GetWeakPtr();
- for (int i = 1; i <= 3; ++i) {
- task_runner()->PostTask([=]() mutable {
- auto* dest = weak_ptr.get();
- // The weak pointer must be reset before TestTask() is called, otherwise
- // there will be a race where the factory could be destructed on main
- // thread while still bound to the task runner sequence.
- weak_ptr.reset();
- dest->TestTask(i);
- });
- }
- wait_for_tasks.Run();
- }
- #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)
- // Tests file descriptor reuse that causes crashes.
- TEST_F(PerfettoTaskRunnerTest, FileDescriptorReuse) {
- int sockets[2];
- // Use sockets because we need a FD that supports epoll().
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
- base::ScopedFD fd(sockets[0]), write_fd(sockets[1]);
- ASSERT_TRUE(fd.is_valid());
- constexpr int data_value = 0x12ab34cd;
- bool run_callback_1 = false, run_callback_2 = false;
- int data = data_value;
- constexpr ssize_t data_size = static_cast<ssize_t>(sizeof(data));
- // Trigger the file descriptor watcher callback.
- ASSERT_EQ(data_size, HANDLE_EINTR(write(write_fd.get(), &data, data_size)));
- data = 0;
- base::RunLoop run_loop;
- task_runner()->GetOrCreateTaskRunner()->PostTask(
- FROM_HERE, base::BindLambdaForTesting([&]() {
- // The 1st add operation posts a task.
- task_runner()->AddFileDescriptorWatch(fd.get(), [&]() {
- run_callback_1 = true;
- ASSERT_EQ(data_size, HANDLE_EINTR(read(fd.get(), &data, data_size)));
- run_loop.Quit();
- });
- // Remove so the 2nd add operation can succeed.
- task_runner()->RemoveFileDescriptorWatch(fd.get());
- // Simulate FD reuse. The 2nd add operation also posts a task.
- task_runner()->AddFileDescriptorWatch(fd.get(), [&]() {
- run_callback_2 = true;
- ASSERT_EQ(data_size, HANDLE_EINTR(read(fd.get(), &data, data_size)));
- run_loop.Quit();
- });
- }));
- // Make all posted tasks run.
- run_loop.Run();
- ASSERT_FALSE(run_callback_1);
- ASSERT_TRUE(run_callback_2);
- ASSERT_EQ(data, data_value);
- task_runner()->GetOrCreateTaskRunner()->PostTask(
- FROM_HERE, base::BindLambdaForTesting([&]() {
- // Cleanup the FD watcher.
- task_runner()->RemoveFileDescriptorWatch(fd.get());
- }));
- task_environment().RunUntilIdle();
- }
- #endif
- } // namespace
- } // namespace tracing
- } // namespace base
|