|  | // Copyright 2018 The Chromium Authors | 
|  | // 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 "base/auto_reset.h" | 
|  | #include "base/containers/contains.h" | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/notreached.h" | 
|  | #include "base/task/common/checked_lock_impl.h" | 
|  | #include "base/task/common/scoped_defer_task_posting.h" | 
|  | #include "base/task/sequenced_task_runner.h" | 
|  | #include "base/task/thread_pool.h" | 
|  | #include "base/task/thread_pool/thread_pool_instance.h" | 
|  | #include "base/tracing/tracing_tls.h" | 
|  | #include "build/build_config.h" | 
|  |  | 
|  | namespace base::tracing { | 
|  |  | 
|  | PerfettoTaskRunner::PerfettoTaskRunner( | 
|  | scoped_refptr<base::SequencedTaskRunner> task_runner, | 
|  | bool defer_delayed_tasks) | 
|  | : task_runner_(std::move(task_runner)), | 
|  | defer_delayed_tasks_(defer_delayed_tasks) { | 
|  | CHECK(task_runner_); | 
|  | } | 
|  |  | 
|  | PerfettoTaskRunner::~PerfettoTaskRunner() { | 
|  | DCHECK(task_runner_->RunsTasksInCurrentSequence()); | 
|  | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | fd_controllers_.clear(); | 
|  | #endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | } | 
|  |  | 
|  | void PerfettoTaskRunner::PostTask(std::function<void()> task) { | 
|  | PostDelayedTask(task, /* delay_ms */ 0); | 
|  | } | 
|  |  | 
|  | void PerfettoTaskRunner::PostDelayedTask(std::function<void()> task, | 
|  | uint32_t delay_ms) { | 
|  | if (defer_delayed_tasks_ && delay_ms) { | 
|  | deferred_delayed_tasks_.emplace_back(task, delay_ms); | 
|  | return; | 
|  | } | 
|  | base::ScopedDeferTaskPosting::PostOrDefer( | 
|  | task_runner_, FROM_HERE, | 
|  | base::BindOnce( | 
|  | [](std::function<void()> task) { | 
|  | // We block any trace events that happens while any | 
|  | // Perfetto task is running, or we'll get deadlocks in | 
|  | // situations where the StartupTraceWriterRegistry tries | 
|  | // to bind a writer which in turn causes a PostTask where | 
|  | // a trace event can be emitted, which then deadlocks as | 
|  | // it needs a new chunk from the same StartupTraceWriter | 
|  | // which we're trying to bind and are keeping the lock | 
|  | // to. | 
|  | // TODO(oysteine): Try to see if we can be more selective | 
|  | // about this. | 
|  | const AutoReset<bool> resetter(GetThreadIsInTraceEvent(), true, | 
|  | false); | 
|  | task(); | 
|  | }, | 
|  | task), | 
|  | base::Milliseconds(delay_ms)); | 
|  | } | 
|  |  | 
|  | bool PerfettoTaskRunner::RunsTasksOnCurrentThread() const { | 
|  | DCHECK(task_runner_); | 
|  | return task_runner_->RunsTasksInCurrentSequence(); | 
|  | } | 
|  |  | 
|  | // PlatformHandle is an int on POSIX, a HANDLE on Windows. | 
|  | void PerfettoTaskRunner::AddFileDescriptorWatch( | 
|  | perfetto::base::PlatformHandle fd, | 
|  | std::function<void()> callback) { | 
|  | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | DCHECK(task_runner_->RunsTasksInCurrentSequence()); | 
|  | DCHECK(!base::Contains(fd_controllers_, fd)); | 
|  | // Set up the |fd| in the map to signal intent to add a watch. We need to | 
|  | // PostTask the WatchReadable creation because if we do it in this task we'll | 
|  | // race with perfetto setting up the connection on this task and the IO thread | 
|  | // setting up epoll on the |fd|. Using a CancelableOnceClosure ensures that | 
|  | // the |fd| won't be added for watch if RemoveFileDescriptorWatch is called. | 
|  | fd_controllers_[fd].callback.Reset(base::BindOnce( | 
|  | [](PerfettoTaskRunner* perfetto_runner, int fd, | 
|  | std::function<void()> callback) { | 
|  | DCHECK(perfetto_runner->task_runner_->RunsTasksInCurrentSequence()); | 
|  | // When this callback runs, we must not have removed |fd|'s watch. | 
|  | CHECK(base::Contains(perfetto_runner->fd_controllers_, fd)); | 
|  | auto& controller_and_cb = perfetto_runner->fd_controllers_[fd]; | 
|  | // We should never overwrite an existing watch. | 
|  | CHECK(!controller_and_cb.controller); | 
|  | controller_and_cb.controller = | 
|  | base::FileDescriptorWatcher::WatchReadable( | 
|  | fd, base::BindRepeating( | 
|  | [](std::function<void()> callback) { callback(); }, | 
|  | std::move(callback))); | 
|  | }, | 
|  | base::Unretained(this), fd, std::move(callback))); | 
|  | task_runner_->PostTask(FROM_HERE, fd_controllers_[fd].callback.callback()); | 
|  | #else   // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | NOTREACHED(); | 
|  | #endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | } | 
|  |  | 
|  | void PerfettoTaskRunner::RemoveFileDescriptorWatch( | 
|  | perfetto::base::PlatformHandle fd) { | 
|  | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | DCHECK(task_runner_->RunsTasksInCurrentSequence()); | 
|  | DCHECK(base::Contains(fd_controllers_, fd)); | 
|  | // This also cancels the base::FileDescriptorWatcher::WatchReadable() task if | 
|  | // it's pending. | 
|  | fd_controllers_.erase(fd); | 
|  | #else   // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | NOTREACHED(); | 
|  | #endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | } | 
|  |  | 
|  | void PerfettoTaskRunner::ResetTaskRunner( | 
|  | scoped_refptr<base::SequencedTaskRunner> task_runner) { | 
|  | task_runner_ = std::move(task_runner); | 
|  | defer_delayed_tasks_ = false; | 
|  | for (auto& task : deferred_delayed_tasks_) { | 
|  | PostDelayedTask(task.task, task.delay); | 
|  | } | 
|  | deferred_delayed_tasks_.clear(); | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  | PerfettoTaskRunner::FDControllerAndCallback::FDControllerAndCallback() = | 
|  | default; | 
|  |  | 
|  | PerfettoTaskRunner::FDControllerAndCallback::~FDControllerAndCallback() = | 
|  | default; | 
|  | #endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) | 
|  |  | 
|  | PerfettoTaskRunner::DeferredTask::DeferredTask(std::function<void()> task, | 
|  | uint32_t delay) | 
|  | : task(std::move(task)), delay(delay) {} | 
|  |  | 
|  | PerfettoTaskRunner::DeferredTask::DeferredTask(DeferredTask&& task) = default; | 
|  |  | 
|  | PerfettoTaskRunner::DeferredTask::~DeferredTask() = default; | 
|  |  | 
|  | }  // namespace base::tracing |