1// Copyright (c) 2012 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 "remoting/base/auto_thread.h"
6
7#include "base/bind.h"
8#include "base/lazy_instance.h"
9#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
10#include "base/threading/thread_local.h"
11#include "base/threading/thread_restrictions.h"
12#include "base/synchronization/waitable_event.h"
13#include "remoting/base/auto_thread_task_runner.h"
14
15#if defined(OS_WIN)
16#include "base/win/scoped_com_initializer.h"
17#endif
18
19namespace remoting {
20
21namespace {
22
23#if defined(OS_WIN)
24scoped_ptr<base::win::ScopedCOMInitializer> CreateComInitializer(
25    AutoThread::ComInitType type) {
26  scoped_ptr<base::win::ScopedCOMInitializer> initializer;
27  if (type == AutoThread::COM_INIT_MTA) {
28    initializer.reset(new base::win::ScopedCOMInitializer(
29        base::win::ScopedCOMInitializer::kMTA));
30  } else if (type == AutoThread::COM_INIT_STA) {
31    initializer.reset(new base::win::ScopedCOMInitializer());
32  }
33  return initializer.Pass();
34}
35#endif
36
37}
38
39// Used to pass data to ThreadMain.  This structure is allocated on the stack
40// from within StartWithType.
41struct AutoThread::StartupData {
42  // Fields describing the desired thread behaviour.
43  base::MessageLoop::Type loop_type;
44
45  // Used to receive the AutoThreadTaskRunner for the thread.
46  scoped_refptr<AutoThreadTaskRunner> task_runner;
47
48  // Used to synchronize thread startup.
49  base::WaitableEvent event;
50
51  explicit StartupData(base::MessageLoop::Type type)
52      : loop_type(type), event(false, false) {}
53};
54
55// static
56scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithType(
57    const char* name,
58    scoped_refptr<AutoThreadTaskRunner> joiner,
59    base::MessageLoop::Type type) {
60  AutoThread* thread = new AutoThread(name, joiner.get());
61  scoped_refptr<AutoThreadTaskRunner> task_runner = thread->StartWithType(type);
62  if (!task_runner.get())
63    delete thread;
64  return task_runner;
65}
66
67// static
68scoped_refptr<AutoThreadTaskRunner> AutoThread::Create(
69    const char* name, scoped_refptr<AutoThreadTaskRunner> joiner) {
70  return CreateWithType(name, joiner, base::MessageLoop::TYPE_DEFAULT);
71}
72
73#if defined(OS_WIN)
74// static
75scoped_refptr<AutoThreadTaskRunner> AutoThread::CreateWithLoopAndComInitTypes(
76    const char* name,
77    scoped_refptr<AutoThreadTaskRunner> joiner,
78    base::MessageLoop::Type loop_type,
79    ComInitType com_init_type) {
80  AutoThread* thread = new AutoThread(name, joiner);
81  thread->SetComInitType(com_init_type);
82  scoped_refptr<AutoThreadTaskRunner> task_runner =
83      thread->StartWithType(loop_type);
84  if (!task_runner)
85    delete thread;
86  return task_runner;
87}
88#endif
89
90AutoThread::AutoThread(const char* name)
91  : startup_data_(NULL),
92#if defined(OS_WIN)
93    com_init_type_(COM_INIT_NONE),
94#endif
95    thread_(),
96    name_(name),
97    was_quit_properly_(false) {
98}
99
100AutoThread::AutoThread(const char* name, AutoThreadTaskRunner* joiner)
101  : startup_data_(NULL),
102#if defined(OS_WIN)
103    com_init_type_(COM_INIT_NONE),
104#endif
105    thread_(),
106    name_(name),
107    was_quit_properly_(false),
108    joiner_(joiner) {
109}
110
111AutoThread::~AutoThread() {
112  DCHECK(!startup_data_);
113
114  // Wait for the thread to exit.
115  if (!thread_.is_null()) {
116    base::PlatformThread::Join(thread_);
117  }
118}
119
120scoped_refptr<AutoThreadTaskRunner> AutoThread::StartWithType(
121    base::MessageLoop::Type type) {
122  DCHECK(thread_.is_null());
123#if defined(OS_WIN)
124  DCHECK(com_init_type_ != COM_INIT_STA || type == base::MessageLoop::TYPE_UI);
125#endif
126
127  StartupData startup_data(type);
128  startup_data_ = &startup_data;
129
130  if (!base::PlatformThread::Create(0, this, &thread_)) {
131    DLOG(ERROR) << "failed to create thread";
132    startup_data_ = NULL;
133    return NULL;
134  }
135
136  // Wait for the thread to start and initialize message_loop_
137  // TODO(wez): Since at this point we know the MessageLoop _will_ run, and
138  // the thread lifetime is controlled by the AutoThreadTaskRunner, we would
139  // ideally return the AutoThreadTaskRunner to the caller without waiting for
140  // the thread to signal us.
141  base::ThreadRestrictions::ScopedAllowWait allow_wait;
142  startup_data.event.Wait();
143
144  // set it to NULL so we don't keep a pointer to some object on the stack.
145  startup_data_ = NULL;
146
147  DCHECK(startup_data.task_runner.get());
148  return startup_data.task_runner;
149}
150
151#if defined(OS_WIN)
152void AutoThread::SetComInitType(ComInitType com_init_type) {
153  DCHECK_EQ(com_init_type_, COM_INIT_NONE);
154  com_init_type_ = com_init_type;
155}
156#endif
157
158void AutoThread::QuitThread(
159    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
160  if (!task_runner->BelongsToCurrentThread()) {
161    task_runner->PostTask(FROM_HERE, base::Bind(&AutoThread::QuitThread,
162                                                base::Unretained(this),
163                                                task_runner));
164    return;
165  }
166
167  base::MessageLoop::current()->Quit();
168  was_quit_properly_ = true;
169
170  if (joiner_.get()) {
171    joiner_->PostTask(
172        FROM_HERE,
173        base::Bind(&AutoThread::JoinAndDeleteThread, base::Unretained(this)));
174  }
175}
176
177void AutoThread::JoinAndDeleteThread() {
178  delete this;
179}
180
181void AutoThread::ThreadMain() {
182  // The message loop for this thread.
183  base::MessageLoop message_loop(startup_data_->loop_type);
184
185  // Complete the initialization of our AutoThread object.
186  base::PlatformThread::SetName(name_.c_str());
187  ANNOTATE_THREAD_NAME(name_.c_str());  // Tell the name to race detector.
188  message_loop.set_thread_name(name_);
189
190  // Return an AutoThreadTaskRunner that will cleanly quit this thread when
191  // no more references to it remain.
192  startup_data_->task_runner =
193      new AutoThreadTaskRunner(message_loop.message_loop_proxy(),
194          base::Bind(&AutoThread::QuitThread,
195                     base::Unretained(this),
196                     message_loop.message_loop_proxy()));
197
198  startup_data_->event.Signal();
199  // startup_data_ can't be touched anymore since the starting thread is now
200  // unlocked.
201
202#if defined(OS_WIN)
203  // Initialize COM on the thread, if requested.
204  scoped_ptr<base::win::ScopedCOMInitializer> com_initializer(
205      CreateComInitializer(com_init_type_));
206#endif
207
208  message_loop.Run();
209
210  // Assert that MessageLoop::Quit was called by AutoThread::QuitThread.
211  DCHECK(was_quit_properly_);
212}
213
214}  // namespace base
215