blob: 576aa9b1bf9360a0949a84e2db5c659ca40c2c53 [file] [log] [blame]
// Copyright 2013 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 "cc/trees/blocking_task_runner.h"
#include <utility>
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop_proxy.h"
namespace cc {
typedef std::pair<base::SingleThreadTaskRunner*,
scoped_refptr<BlockingTaskRunner> > TaskRunnerPair;
struct TaskRunnerPairs {
static TaskRunnerPairs* GetInstance() {
return Singleton<TaskRunnerPairs>::get();
}
base::Lock lock;
std::vector<TaskRunnerPair> pairs;
private:
friend struct DefaultSingletonTraits<TaskRunnerPairs>;
};
// static
scoped_refptr<BlockingTaskRunner> BlockingTaskRunner::current() {
TaskRunnerPairs* task_runners = TaskRunnerPairs::GetInstance();
base::AutoLock lock(task_runners->lock);
for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
if (task_runners->pairs[i].first->HasOneRef()) {
// The SingleThreadTaskRunner is kept alive by its MessageLoop, and we
// hold a second reference in the TaskRunnerPairs array. If the
// SingleThreadTaskRunner has one ref, then it is being held alive only
// by the BlockingTaskRunner and the MessageLoop is gone, so drop the
// BlockingTaskRunner from the TaskRunnerPairs array along with the
// SingleThreadTaskRunner.
task_runners->pairs.erase(task_runners->pairs.begin() + i);
--i;
}
}
scoped_refptr<base::SingleThreadTaskRunner> current =
base::MessageLoopProxy::current();
for (size_t i = 0; i < task_runners->pairs.size(); ++i) {
if (task_runners->pairs[i].first == current.get())
return task_runners->pairs[i].second.get();
}
scoped_refptr<BlockingTaskRunner> runner = new BlockingTaskRunner(current);
task_runners->pairs.push_back(TaskRunnerPair(current, runner));
return runner;
}
BlockingTaskRunner::BlockingTaskRunner(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: task_runner_(task_runner), capture_(0) {}
BlockingTaskRunner::~BlockingTaskRunner() {}
bool BlockingTaskRunner::PostTask(const tracked_objects::Location& from_here,
const base::Closure& task) {
base::AutoLock lock(lock_);
if (!capture_)
return task_runner_->PostTask(from_here, task);
captured_tasks_.push_back(task);
return true;
}
void BlockingTaskRunner::SetCapture(bool capture) {
DCHECK(BelongsToCurrentThread());
std::vector<base::Closure> tasks;
{
base::AutoLock lock(lock_);
capture_ += capture ? 1 : -1;
DCHECK_GE(capture_, 0);
if (capture_)
return;
// We're done capturing, so grab all the captured tasks and run them.
tasks.swap(captured_tasks_);
}
for (size_t i = 0; i < tasks.size(); ++i)
tasks[i].Run();
}
BlockingTaskRunner::CapturePostTasks::CapturePostTasks()
: blocking_runner_(BlockingTaskRunner::current()) {
blocking_runner_->SetCapture(true);
}
BlockingTaskRunner::CapturePostTasks::~CapturePostTasks() {
blocking_runner_->SetCapture(false);
}
} // namespace cc