ipc/chromium/src/base/thread.cc

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

mercurial