1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/chromium/src/base/thread.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,188 @@ 1.4 +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "base/thread.h" 1.9 + 1.10 +#include "base/lazy_instance.h" 1.11 +#include "base/string_util.h" 1.12 +#include "base/thread_local.h" 1.13 +#include "base/waitable_event.h" 1.14 +#include "GeckoProfiler.h" 1.15 +#include "mozilla/IOInterposer.h" 1.16 + 1.17 +#ifdef MOZ_TASK_TRACER 1.18 +#include "GeckoTaskTracer.h" 1.19 +#endif 1.20 + 1.21 +namespace base { 1.22 + 1.23 +// This task is used to trigger the message loop to exit. 1.24 +class ThreadQuitTask : public Task { 1.25 + public: 1.26 + virtual void Run() { 1.27 + MessageLoop::current()->Quit(); 1.28 + Thread::SetThreadWasQuitProperly(true); 1.29 + } 1.30 +}; 1.31 + 1.32 +// Used to pass data to ThreadMain. This structure is allocated on the stack 1.33 +// from within StartWithOptions. 1.34 +struct Thread::StartupData { 1.35 + // We get away with a const reference here because of how we are allocated. 1.36 + const Thread::Options& options; 1.37 + 1.38 + // Used to synchronize thread startup. 1.39 + WaitableEvent event; 1.40 + 1.41 + explicit StartupData(const Options& opt) 1.42 + : options(opt), 1.43 + event(false, false) {} 1.44 +}; 1.45 + 1.46 +Thread::Thread(const char *name) 1.47 + : startup_data_(NULL), 1.48 + thread_(0), 1.49 + message_loop_(NULL), 1.50 + thread_id_(0), 1.51 + name_(name) { 1.52 +} 1.53 + 1.54 +Thread::~Thread() { 1.55 + Stop(); 1.56 +} 1.57 + 1.58 +namespace { 1.59 + 1.60 +// We use this thread-local variable to record whether or not a thread exited 1.61 +// because its Stop method was called. This allows us to catch cases where 1.62 +// MessageLoop::Quit() is called directly, which is unexpected when using a 1.63 +// Thread to setup and run a MessageLoop. 1.64 +base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool( 1.65 + base::LINKER_INITIALIZED); 1.66 + 1.67 +} // namespace 1.68 + 1.69 +void Thread::SetThreadWasQuitProperly(bool flag) { 1.70 + lazy_tls_bool.Pointer()->Set(flag); 1.71 +} 1.72 + 1.73 +bool Thread::GetThreadWasQuitProperly() { 1.74 + bool quit_properly = true; 1.75 +#ifndef NDEBUG 1.76 + quit_properly = lazy_tls_bool.Pointer()->Get(); 1.77 +#endif 1.78 + return quit_properly; 1.79 +} 1.80 + 1.81 +bool Thread::Start() { 1.82 + return StartWithOptions(Options()); 1.83 +} 1.84 + 1.85 +bool Thread::StartWithOptions(const Options& options) { 1.86 + DCHECK(!message_loop_); 1.87 + 1.88 + SetThreadWasQuitProperly(false); 1.89 + 1.90 + StartupData startup_data(options); 1.91 + startup_data_ = &startup_data; 1.92 + 1.93 + if (!PlatformThread::Create(options.stack_size, this, &thread_)) { 1.94 + DLOG(ERROR) << "failed to create thread"; 1.95 + startup_data_ = NULL; // Record that we failed to start. 1.96 + return false; 1.97 + } 1.98 + 1.99 + // Wait for the thread to start and initialize message_loop_ 1.100 + startup_data.event.Wait(); 1.101 + 1.102 + DCHECK(message_loop_); 1.103 + return true; 1.104 +} 1.105 + 1.106 +void Thread::Stop() { 1.107 + if (!thread_was_started()) 1.108 + return; 1.109 + 1.110 + // We should only be called on the same thread that started us. 1.111 + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); 1.112 + 1.113 + // StopSoon may have already been called. 1.114 + if (message_loop_) 1.115 + message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); 1.116 + 1.117 + // Wait for the thread to exit. It should already have terminated but make 1.118 + // sure this assumption is valid. 1.119 + // 1.120 + // TODO(darin): Unfortunately, we need to keep message_loop_ around until 1.121 + // the thread exits. Some consumers are abusing the API. Make them stop. 1.122 + // 1.123 + PlatformThread::Join(thread_); 1.124 + 1.125 + // The thread can't receive messages anymore. 1.126 + message_loop_ = NULL; 1.127 + 1.128 + // The thread no longer needs to be joined. 1.129 + startup_data_ = NULL; 1.130 +} 1.131 + 1.132 +void Thread::StopSoon() { 1.133 + if (!message_loop_) 1.134 + return; 1.135 + 1.136 + // We should only be called on the same thread that started us. 1.137 + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); 1.138 + 1.139 + // We had better have a message loop at this point! If we do not, then it 1.140 + // most likely means that the thread terminated unexpectedly, probably due 1.141 + // to someone calling Quit() on our message loop directly. 1.142 + DCHECK(message_loop_); 1.143 + 1.144 + message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); 1.145 +} 1.146 + 1.147 +void Thread::ThreadMain() { 1.148 + char aLocal; 1.149 + profiler_register_thread(name_.c_str(), &aLocal); 1.150 + mozilla::IOInterposer::RegisterCurrentThread(); 1.151 + 1.152 + // The message loop for this thread. 1.153 + MessageLoop message_loop(startup_data_->options.message_loop_type); 1.154 + 1.155 + // Complete the initialization of our Thread object. 1.156 + thread_id_ = PlatformThread::CurrentId(); 1.157 + PlatformThread::SetName(name_.c_str()); 1.158 + message_loop.set_thread_name(name_); 1.159 + message_loop.set_hang_timeouts(startup_data_->options.transient_hang_timeout, 1.160 + startup_data_->options.permanent_hang_timeout); 1.161 + message_loop_ = &message_loop; 1.162 + 1.163 + // Let the thread do extra initialization. 1.164 + // Let's do this before signaling we are started. 1.165 + Init(); 1.166 + 1.167 + startup_data_->event.Signal(); 1.168 + // startup_data_ can't be touched anymore since the starting thread is now 1.169 + // unlocked. 1.170 + 1.171 + message_loop.Run(); 1.172 + 1.173 + // Let the thread do extra cleanup. 1.174 + CleanUp(); 1.175 + 1.176 + // Assert that MessageLoop::Quit was called by ThreadQuitTask. 1.177 + DCHECK(GetThreadWasQuitProperly()); 1.178 + 1.179 + mozilla::IOInterposer::UnregisterCurrentThread(); 1.180 + profiler_unregister_thread(); 1.181 + 1.182 +#ifdef MOZ_TASK_TRACER 1.183 + mozilla::tasktracer::FreeTraceInfo(); 1.184 +#endif 1.185 + 1.186 + // We can't receive messages anymore. 1.187 + message_loop_ = NULL; 1.188 + thread_id_ = 0; 1.189 +} 1.190 + 1.191 +} // namespace base