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