ipc/chromium/src/base/thread.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial