| Index: base/profiler/stack_sampling_profiler.cc
|
| diff --git a/base/profiler/stack_sampling_profiler.cc b/base/profiler/stack_sampling_profiler.cc
|
| index ce898d4c07f7a797455419f1e7bd2e64fdf05581..91cc193f23d77dcf256578df0695a1e1bb48b47d 100644
|
| --- a/base/profiler/stack_sampling_profiler.cc
|
| +++ b/base/profiler/stack_sampling_profiler.cc
|
| @@ -28,63 +28,6 @@ namespace base {
|
|
|
| namespace {
|
|
|
| -// AsyncRunner ----------------------------------------------------------------
|
| -
|
| -// Helper class to allow a profiler to be run completely asynchronously from the
|
| -// initiator, without being concerned with the profiler's lifetime.
|
| -class AsyncRunner {
|
| - public:
|
| - // Sets up a profiler and arranges for it to be deleted on its completed
|
| - // callback.
|
| - static void Run(PlatformThreadId thread_id,
|
| - const StackSamplingProfiler::SamplingParams& params,
|
| - const StackSamplingProfiler::CompletedCallback& callback);
|
| -
|
| - private:
|
| - AsyncRunner();
|
| -
|
| - // Runs the callback and deletes the AsyncRunner instance. |profiles| is not
|
| - // const& because it must be passed with std::move.
|
| - static void RunCallbackAndDeleteInstance(
|
| - std::unique_ptr<AsyncRunner> object_to_be_deleted,
|
| - const StackSamplingProfiler::CompletedCallback& callback,
|
| - scoped_refptr<SingleThreadTaskRunner> task_runner,
|
| - StackSamplingProfiler::CallStackProfiles profiles);
|
| -
|
| - std::unique_ptr<StackSamplingProfiler> profiler_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(AsyncRunner);
|
| -};
|
| -
|
| -// static
|
| -void AsyncRunner::Run(
|
| - PlatformThreadId thread_id,
|
| - const StackSamplingProfiler::SamplingParams& params,
|
| - const StackSamplingProfiler::CompletedCallback &callback) {
|
| - std::unique_ptr<AsyncRunner> runner(new AsyncRunner);
|
| - AsyncRunner* temp_ptr = runner.get();
|
| - temp_ptr->profiler_.reset(
|
| - new StackSamplingProfiler(thread_id, params,
|
| - Bind(&AsyncRunner::RunCallbackAndDeleteInstance,
|
| - Passed(&runner), callback,
|
| - ThreadTaskRunnerHandle::Get())));
|
| - // The callback won't be called until after Start(), so temp_ptr will still
|
| - // be valid here.
|
| - temp_ptr->profiler_->Start();
|
| -}
|
| -
|
| -AsyncRunner::AsyncRunner() {}
|
| -
|
| -void AsyncRunner::RunCallbackAndDeleteInstance(
|
| - std::unique_ptr<AsyncRunner> object_to_be_deleted,
|
| - const StackSamplingProfiler::CompletedCallback& callback,
|
| - scoped_refptr<SingleThreadTaskRunner> task_runner,
|
| - StackSamplingProfiler::CallStackProfiles profiles) {
|
| - callback.Run(std::move(profiles));
|
| - // Delete the instance on the original calling thread.
|
| - task_runner->DeleteSoon(FROM_HERE, object_to_be_deleted.release());
|
| -}
|
| -
|
| void ChangeAtomicFlags(subtle::Atomic32* flags,
|
| subtle::Atomic32 set,
|
| subtle::Atomic32 clear) {
|
| @@ -216,8 +159,9 @@ class StackSamplingProfiler::SamplingThread : public Thread {
|
|
|
| // Removes an active collection based on its ID, forcing it to run its
|
| // callback if any data has been collected. This can be called externally
|
| - // from any thread.
|
| - void Remove(int id);
|
| + // from any thread. The |done| event, if not null, will be signaled upon
|
| + // completion. |done| must live until after it has been signaled.
|
| + void Remove(int id, WaitableEvent* done);
|
|
|
| // Removes all active collections and stops the underlying thread.
|
| void Shutdown();
|
| @@ -255,7 +199,7 @@ class StackSamplingProfiler::SamplingThread : public Thread {
|
|
|
| // These methods are tasks that get posted to the internal message queue.
|
| void AddCollectionTask(std::unique_ptr<CollectionContext> collection_ptr);
|
| - void RemoveCollectionTask(int id);
|
| + void RemoveCollectionTask(int id, WaitableEvent* done);
|
| void PerformCollectionTask(int id);
|
| void ShutdownTask();
|
|
|
| @@ -320,16 +264,24 @@ int StackSamplingProfiler::SamplingThread::Add(
|
| return id;
|
| }
|
|
|
| -void StackSamplingProfiler::SamplingThread::Remove(int id) {
|
| +void StackSamplingProfiler::SamplingThread::Remove(int id,
|
| + WaitableEvent* done) {
|
| scoped_refptr<SingleThreadTaskRunner> task_runner = GetTaskRunner();
|
| - if (!task_runner)
|
| - return; // Everything has already stopped.
|
| + if (task_runner) {
|
| + // This can fail if the thread were to exit between acquisition of the task
|
| + // runner above and the call below. Because there may be something waiting
|
| + // for a signal on the |done| event, return only if the task was success-
|
| + // fully posted.
|
| + if (task_runner->PostTask(FROM_HERE,
|
| + Bind(&SamplingThread::RemoveCollectionTask,
|
| + Unretained(this), id, Unretained(done)))) {
|
| + return;
|
| + }
|
| + }
|
|
|
| - // This can fail if the thread were to exit between acquisition of the task
|
| - // runner above and the call below. In that case, however, everything has
|
| - // stopped so there's no need to try to stop it.
|
| - task_runner->PostTask(FROM_HERE, Bind(&SamplingThread::RemoveCollectionTask,
|
| - Unretained(this), id));
|
| + // The sampling thread has already exited so signal the event here.
|
| + if (done)
|
| + done->Signal();
|
| }
|
|
|
| void StackSamplingProfiler::SamplingThread::Shutdown() {
|
| @@ -514,13 +466,17 @@ void StackSamplingProfiler::SamplingThread::AddCollectionTask(
|
| collection->params.initial_delay);
|
| }
|
|
|
| -void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(int id) {
|
| +void StackSamplingProfiler::SamplingThread::RemoveCollectionTask(
|
| + int id,
|
| + WaitableEvent* done) {
|
| auto found = active_collections_.find(id);
|
| - if (found == active_collections_.end())
|
| - return;
|
| + if (found != active_collections_.end()) {
|
| + FinishCollection(found->second.get());
|
| + CheckForIdle();
|
| + }
|
|
|
| - FinishCollection(found->second.get());
|
| - CheckForIdle();
|
| + if (done)
|
| + done->Signal();
|
| }
|
|
|
| void StackSamplingProfiler::SamplingThread::PerformCollectionTask(int id) {
|
| @@ -658,31 +614,10 @@ StackSamplingProfiler::StackSamplingProfiler(
|
| }
|
|
|
| StackSamplingProfiler::~StackSamplingProfiler() {
|
| - Stop();
|
| -
|
| -#if !defined(OS_WIN)
|
| - // This is to prevent use of this object on non-Windows builds until such
|
| - // time as the following race condition is addressed:
|
| - // Thread A creates a local (stack) StackSamplingProfiler.
|
| - // Sampling is initiated against thread A ("A samples A").
|
| - // The StackSamplingProfiler is destructed and thread A exits.
|
| - // Sampling continues briefly until the async Stop() gets through.
|
| - // Another thread with the same ID replaces the first one.
|
| - // Sampling access the new thread and Bad Things(tm) happen.
|
| - // Windows is immune to this because the thread handle held internally
|
| - // prevents re-use of the thread-ID but this may not be the case on other
|
| - // platforms.
|
| - NOTREACHED();
|
| -#endif
|
| -}
|
| -
|
| -// static
|
| -void StackSamplingProfiler::StartAndRunAsync(
|
| - PlatformThreadId thread_id,
|
| - const SamplingParams& params,
|
| - const CompletedCallback& callback) {
|
| - CHECK(ThreadTaskRunnerHandle::Get());
|
| - AsyncRunner::Run(thread_id, params, callback);
|
| + WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL,
|
| + WaitableEvent::InitialState::NOT_SIGNALED);
|
| + Stop(&done);
|
| + done.Wait();
|
| }
|
|
|
| void StackSamplingProfiler::Start() {
|
| @@ -698,8 +633,8 @@ void StackSamplingProfiler::Start() {
|
| std::move(native_sampler_)));
|
| }
|
|
|
| -void StackSamplingProfiler::Stop() {
|
| - SamplingThread::GetInstance()->Remove(collection_id_);
|
| +void StackSamplingProfiler::Stop(WaitableEvent* done) {
|
| + SamplingThread::GetInstance()->Remove(collection_id_, done);
|
| }
|
|
|
| // static
|
|
|