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