1// Copyright 2014 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 "content/child/worker_task_runner.h"
6
7#include "base/callback.h"
8#include "base/lazy_instance.h"
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/observer_list.h"
12
13using blink::WebWorkerRunLoop;
14
15namespace content {
16
17namespace {
18
19class RunClosureTask : public WebWorkerRunLoop::Task {
20 public:
21  RunClosureTask(const base::Closure& task) : task_(task) {}
22  virtual ~RunClosureTask() {}
23  virtual void Run() {
24    task_.Run();
25  }
26 private:
27  base::Closure task_;
28};
29
30} // namespace
31
32struct WorkerTaskRunner::ThreadLocalState {
33  ThreadLocalState(int id, const WebWorkerRunLoop& loop)
34      : id_(id), run_loop_(loop) {
35  }
36  int id_;
37  WebWorkerRunLoop run_loop_;
38  ObserverList<WorkerTaskRunner::Observer> stop_observers_;
39};
40
41WorkerTaskRunner::WorkerTaskRunner() {
42  // Start worker ids at 1, 0 is reserved for the main thread.
43  int id = id_sequence_.GetNext();
44  DCHECK(!id);
45}
46
47bool WorkerTaskRunner::PostTask(
48    int id, const base::Closure& closure) {
49  DCHECK(id > 0);
50  base::AutoLock locker(loop_map_lock_);
51  IDToLoopMap::iterator found = loop_map_.find(id);
52  if (found == loop_map_.end())
53    return false;
54  return found->second.postTask(new RunClosureTask(closure));
55}
56
57int WorkerTaskRunner::PostTaskToAllThreads(const base::Closure& closure) {
58  base::AutoLock locker(loop_map_lock_);
59  IDToLoopMap::iterator it;
60  for (it = loop_map_.begin(); it != loop_map_.end(); ++it)
61    it->second.postTask(new RunClosureTask(closure));
62  return static_cast<int>(loop_map_.size());
63}
64
65int WorkerTaskRunner::CurrentWorkerId() {
66  if (!current_tls_.Get())
67    return 0;
68  return current_tls_.Get()->id_;
69}
70
71WorkerTaskRunner* WorkerTaskRunner::Instance() {
72  static base::LazyInstance<WorkerTaskRunner>::Leaky
73      worker_task_runner = LAZY_INSTANCE_INITIALIZER;
74  return worker_task_runner.Pointer();
75}
76
77void WorkerTaskRunner::AddStopObserver(Observer* obs) {
78  DCHECK(CurrentWorkerId() > 0);
79  current_tls_.Get()->stop_observers_.AddObserver(obs);
80}
81
82void WorkerTaskRunner::RemoveStopObserver(Observer* obs) {
83  DCHECK(CurrentWorkerId() > 0);
84  current_tls_.Get()->stop_observers_.RemoveObserver(obs);
85}
86
87WorkerTaskRunner::~WorkerTaskRunner() {
88}
89
90void WorkerTaskRunner::OnWorkerRunLoopStarted(const WebWorkerRunLoop& loop) {
91  DCHECK(!current_tls_.Get());
92  int id = id_sequence_.GetNext();
93  current_tls_.Set(new ThreadLocalState(id, loop));
94
95  base::AutoLock locker_(loop_map_lock_);
96  loop_map_[id] = loop;
97}
98
99void WorkerTaskRunner::OnWorkerRunLoopStopped(const WebWorkerRunLoop& loop) {
100  DCHECK(current_tls_.Get());
101  FOR_EACH_OBSERVER(Observer, current_tls_.Get()->stop_observers_,
102                    OnWorkerRunLoopStopped());
103  {
104    base::AutoLock locker(loop_map_lock_);
105    DCHECK(loop_map_[CurrentWorkerId()] == loop);
106    loop_map_.erase(CurrentWorkerId());
107  }
108  delete current_tls_.Get();
109  current_tls_.Set(NULL);
110}
111
112}  // namespace content
113