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