thread.cc (5919B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 #include "base/thread.h" 8 9 #include "base/string_util.h" 10 #include "base/thread_local.h" 11 #include "base/waitable_event.h" 12 #include "GeckoProfiler.h" 13 #include "mozilla/EventQueue.h" 14 #include "mozilla/IOInterposer.h" 15 #include "mozilla/ThreadEventQueue.h" 16 #include "nsThreadUtils.h" 17 #include "nsThreadManager.h" 18 19 namespace base { 20 21 // This task is used to trigger the message loop to exit. 22 class ThreadQuitTask : public mozilla::Runnable { 23 public: 24 ThreadQuitTask() : mozilla::Runnable("ThreadQuitTask") {} 25 NS_IMETHOD Run() override { 26 MessageLoop::current()->Quit(); 27 Thread::SetThreadWasQuitProperly(true); 28 return NS_OK; 29 } 30 }; 31 32 // Used to pass data to ThreadMain. This structure is allocated on the stack 33 // from within StartWithOptions. 34 struct Thread::StartupData { 35 // We get away with a const reference here because of how we are allocated. 36 const Thread::Options& options; 37 38 // Used to synchronize thread startup. 39 WaitableEvent event; 40 41 explicit StartupData(const Options& opt) 42 : options(opt), event(false, false) {} 43 }; 44 45 Thread::Thread(const char* name) 46 : startup_data_(NULL), 47 thread_(0), 48 message_loop_(NULL), 49 thread_id_(0), 50 name_(name) { 51 MOZ_COUNT_CTOR(base::Thread); 52 } 53 54 Thread::~Thread() { 55 MOZ_COUNT_DTOR(base::Thread); 56 Stop(); 57 } 58 59 namespace { 60 61 // We use this thread-local variable to record whether or not a thread exited 62 // because its Stop method was called. This allows us to catch cases where 63 // MessageLoop::Quit() is called directly, which is unexpected when using a 64 // Thread to setup and run a MessageLoop. 65 66 static base::ThreadLocalBoolean& get_tls_bool() { 67 static base::ThreadLocalBoolean tls_ptr; 68 return tls_ptr; 69 } 70 71 } // namespace 72 73 void Thread::SetThreadWasQuitProperly(bool flag) { get_tls_bool().Set(flag); } 74 75 bool Thread::GetThreadWasQuitProperly() { 76 bool quit_properly = true; 77 #ifndef NDEBUG 78 quit_properly = get_tls_bool().Get(); 79 #endif 80 return quit_properly; 81 } 82 83 bool Thread::Start() { return StartWithOptions(Options()); } 84 85 bool Thread::StartWithOptions(const Options& options) { 86 DCHECK(!message_loop_); 87 88 SetThreadWasQuitProperly(false); 89 90 StartupData startup_data(options); 91 startup_data_ = &startup_data; 92 93 if (!PlatformThread::Create(options.stack_size, this, &thread_)) { 94 DLOG(ERROR) << "failed to create thread"; 95 startup_data_ = NULL; // Record that we failed to start. 96 return false; 97 } 98 99 // Wait for the thread to start and initialize message_loop_ 100 startup_data.event.Wait(); 101 102 DCHECK(message_loop_); 103 return true; 104 } 105 106 void Thread::Stop() { 107 if (!thread_was_started()) return; 108 109 // We should only be called on the same thread that started us. 110 DCHECK_NE(thread_id_, PlatformThread::CurrentId()); 111 112 // StopSoon may have already been called. 113 if (message_loop_) { 114 RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); 115 message_loop_->PostTask(task.forget()); 116 } 117 118 // Wait for the thread to exit. It should already have terminated but make 119 // sure this assumption is valid. 120 // 121 // TODO(darin): Unfortunately, we need to keep message_loop_ around until 122 // the thread exits. Some consumers are abusing the API. Make them stop. 123 // 124 PlatformThread::Join(thread_); 125 126 // The thread can't receive messages anymore. 127 message_loop_ = NULL; 128 129 // The thread no longer needs to be joined. 130 startup_data_ = NULL; 131 } 132 133 void Thread::StopSoon() { 134 if (!message_loop_) return; 135 136 // We should only be called on the same thread that started us. 137 DCHECK_NE(thread_id_, PlatformThread::CurrentId()); 138 139 // We had better have a message loop at this point! If we do not, then it 140 // most likely means that the thread terminated unexpectedly, probably due 141 // to someone calling Quit() on our message loop directly. 142 DCHECK(message_loop_); 143 144 RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); 145 message_loop_->PostTask(task.forget()); 146 } 147 148 void Thread::ThreadMain() { 149 nsCOMPtr<nsIThread> xpcomThread; 150 auto loopType = startup_data_->options.message_loop_type; 151 if (loopType == MessageLoop::TYPE_MOZILLA_NONMAINTHREAD || 152 loopType == MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD) { 153 auto queue = mozilla::MakeRefPtr<mozilla::ThreadEventQueue>( 154 mozilla::MakeUnique<mozilla::EventQueue>()); 155 xpcomThread = nsThreadManager::get().CreateCurrentThread(queue); 156 } else { 157 xpcomThread = NS_GetCurrentThread(); 158 } 159 160 AUTO_PROFILER_REGISTER_THREAD(name_.c_str()); 161 mozilla::IOInterposer::RegisterCurrentThread(); 162 163 // The message loop for this thread. 164 MessageLoop message_loop(startup_data_->options.message_loop_type, 165 xpcomThread); 166 167 xpcomThread = nullptr; 168 169 // Complete the initialization of our Thread object. 170 thread_id_ = PlatformThread::CurrentId(); 171 PlatformThread::SetName(name_.c_str()); 172 NS_SetCurrentThreadName(name_.c_str()); 173 message_loop.set_thread_name(name_); 174 message_loop.set_hang_timeouts(startup_data_->options.transient_hang_timeout, 175 startup_data_->options.permanent_hang_timeout); 176 message_loop_ = &message_loop; 177 178 // Let the thread do extra initialization. 179 // Let's do this before signaling we are started. 180 Init(); 181 182 startup_data_->event.Signal(); 183 // startup_data_ can't be touched anymore since the starting thread is now 184 // unlocked. 185 186 message_loop.Run(); 187 188 // Let the thread do extra cleanup. 189 CleanUp(); 190 191 // Assert that MessageLoop::Quit was called by ThreadQuitTask. 192 DCHECK(GetThreadWasQuitProperly()); 193 194 mozilla::IOInterposer::UnregisterCurrentThread(); 195 196 // We can't receive messages anymore. 197 message_loop_ = NULL; 198 thread_id_ = 0; 199 } 200 201 } // namespace base