1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/trees/blocking_task_runner.h"
6
7#include <utility>
8
9#include "base/logging.h"
10#include "base/memory/singleton.h"
11#include "base/message_loop/message_loop_proxy.h"
12
13namespace cc {
14
15struct TaskRunnerPairs {
16  static TaskRunnerPairs* GetInstance() {
17    return Singleton<TaskRunnerPairs>::get();
18  }
19
20  base::Lock lock;
21  std::vector<scoped_refptr<BlockingTaskRunner> > runners;
22
23 private:
24  friend struct DefaultSingletonTraits<TaskRunnerPairs>;
25};
26
27// static
28scoped_refptr<BlockingTaskRunner> BlockingTaskRunner::current() {
29  TaskRunnerPairs* task_runners = TaskRunnerPairs::GetInstance();
30  base::PlatformThreadId thread_id = base::PlatformThread::CurrentId();
31
32  base::AutoLock lock(task_runners->lock);
33
34  scoped_refptr<BlockingTaskRunner> current_task_runner;
35
36  for (size_t i = 0; i < task_runners->runners.size(); ++i) {
37    if (task_runners->runners[i]->thread_id_ == thread_id) {
38      current_task_runner = task_runners->runners[i];
39    } else if (task_runners->runners[i]->HasOneRef()) {
40      task_runners->runners.erase(task_runners->runners.begin() + i);
41      i--;
42    }
43  }
44
45  if (current_task_runner)
46    return current_task_runner;
47
48  scoped_refptr<BlockingTaskRunner> runner =
49      new BlockingTaskRunner(base::MessageLoopProxy::current());
50  task_runners->runners.push_back(runner);
51  return runner;
52}
53
54BlockingTaskRunner::BlockingTaskRunner(
55    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
56    : thread_id_(base::PlatformThread::CurrentId()),
57      task_runner_(task_runner),
58      capture_(0) {
59}
60
61BlockingTaskRunner::~BlockingTaskRunner() {}
62
63bool BlockingTaskRunner::BelongsToCurrentThread() {
64  return base::PlatformThread::CurrentId() == thread_id_;
65}
66
67bool BlockingTaskRunner::PostTask(const tracked_objects::Location& from_here,
68                                  const base::Closure& task) {
69  base::AutoLock lock(lock_);
70  DCHECK(task_runner_.get() || capture_);
71  if (!capture_)
72    return task_runner_->PostTask(from_here, task);
73  captured_tasks_.push_back(task);
74  return true;
75}
76
77void BlockingTaskRunner::SetCapture(bool capture) {
78  DCHECK(BelongsToCurrentThread());
79
80  std::vector<base::Closure> tasks;
81
82  {
83    base::AutoLock lock(lock_);
84    capture_ += capture ? 1 : -1;
85    DCHECK_GE(capture_, 0);
86
87    if (capture_)
88      return;
89
90    // We're done capturing, so grab all the captured tasks and run them.
91    tasks.swap(captured_tasks_);
92  }
93  for (size_t i = 0; i < tasks.size(); ++i)
94    tasks[i].Run();
95}
96
97BlockingTaskRunner::CapturePostTasks::CapturePostTasks()
98    : blocking_runner_(BlockingTaskRunner::current()) {
99  blocking_runner_->SetCapture(true);
100}
101
102BlockingTaskRunner::CapturePostTasks::~CapturePostTasks() {
103  blocking_runner_->SetCapture(false);
104}
105
106}  // namespace cc
107