Home | History | Annotate | Download | only in base
      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 
     19 namespace remoting {
     20 
     21 namespace {
     22 
     23 #if defined(OS_WIN)
     24 scoped_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.
     41 struct 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
     56 scoped_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
     68 scoped_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
     75 scoped_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 
     90 AutoThread::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 
    100 AutoThread::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 
    111 AutoThread::~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 
    120 scoped_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)
    152 void 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 
    158 void 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 
    177 void AutoThread::JoinAndDeleteThread() {
    178   delete this;
    179 }
    180 
    181 void 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