Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(771)

Side by Side Diff: base/threading/thread.cc

Issue 1140363002: Revert of Reland: Lazily initialize MessageLoop for faster thread startup (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/threading/thread.h ('k') | base/threading/thread_id_name_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/threading/thread.h" 5 #include "base/threading/thread.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/lazy_instance.h" 8 #include "base/lazy_instance.h"
9 #include "base/location.h" 9 #include "base/location.h"
10 #include "base/profiler/scoped_tracker.h" 10 #include "base/profiler/scoped_tracker.h"
(...skipping 19 matching lines...) Expand all
30 LAZY_INSTANCE_INITIALIZER; 30 LAZY_INSTANCE_INITIALIZER;
31 31
32 } // namespace 32 } // namespace
33 33
34 // This is used to trigger the message loop to exit. 34 // This is used to trigger the message loop to exit.
35 void ThreadQuitHelper() { 35 void ThreadQuitHelper() {
36 MessageLoop::current()->QuitWhenIdle(); 36 MessageLoop::current()->QuitWhenIdle();
37 Thread::SetThreadWasQuitProperly(true); 37 Thread::SetThreadWasQuitProperly(true);
38 } 38 }
39 39
40 // Used to pass data to ThreadMain. This structure is allocated on the stack
41 // from within StartWithOptions.
42 struct Thread::StartupData {
43 // We get away with a const reference here because of how we are allocated.
44 const Thread::Options& options;
45
46 // Used to synchronize thread startup.
47 WaitableEvent event;
48
49 explicit StartupData(const Options& opt)
50 : options(opt),
51 event(false, false) {}
52 };
53
40 Thread::Options::Options() 54 Thread::Options::Options()
41 : message_loop_type(MessageLoop::TYPE_DEFAULT), 55 : message_loop_type(MessageLoop::TYPE_DEFAULT),
42 timer_slack(TIMER_SLACK_NONE), 56 timer_slack(TIMER_SLACK_NONE),
43 stack_size(0) { 57 stack_size(0) {
44 } 58 }
45 59
46 Thread::Options::Options(MessageLoop::Type type, 60 Thread::Options::Options(MessageLoop::Type type,
47 size_t size) 61 size_t size)
48 : message_loop_type(type), 62 : message_loop_type(type),
49 timer_slack(TIMER_SLACK_NONE), 63 timer_slack(TIMER_SLACK_NONE),
50 stack_size(size) { 64 stack_size(size) {
51 } 65 }
52 66
53 Thread::Options::~Options() { 67 Thread::Options::~Options() {
54 } 68 }
55 69
56 Thread::Thread(const std::string& name) 70 Thread::Thread(const std::string& name)
57 : 71 :
58 #if defined(OS_WIN) 72 #if defined(OS_WIN)
59 com_status_(NONE), 73 com_status_(NONE),
60 #endif 74 #endif
75 started_(false),
61 stopping_(false), 76 stopping_(false),
62 running_(false), 77 running_(false),
78 startup_data_(NULL),
63 thread_(0), 79 thread_(0),
64 message_loop_(nullptr), 80 message_loop_(NULL),
65 message_loop_timer_slack_(TIMER_SLACK_NONE), 81 thread_id_(kInvalidThreadId),
66 name_(name) { 82 name_(name) {
67 } 83 }
68 84
69 Thread::~Thread() { 85 Thread::~Thread() {
70 Stop(); 86 Stop();
71 } 87 }
72 88
73 bool Thread::Start() { 89 bool Thread::Start() {
74 Options options; 90 Options options;
75 #if defined(OS_WIN) 91 #if defined(OS_WIN)
76 if (com_status_ == STA) 92 if (com_status_ == STA)
77 options.message_loop_type = MessageLoop::TYPE_UI; 93 options.message_loop_type = MessageLoop::TYPE_UI;
78 #endif 94 #endif
79 return StartWithOptions(options); 95 return StartWithOptions(options);
80 } 96 }
81 97
82 bool Thread::StartWithOptions(const Options& options) { 98 bool Thread::StartWithOptions(const Options& options) {
83 DCHECK(!message_loop_); 99 DCHECK(!message_loop_);
84 #if defined(OS_WIN) 100 #if defined(OS_WIN)
85 DCHECK((com_status_ != STA) || 101 DCHECK((com_status_ != STA) ||
86 (options.message_loop_type == MessageLoop::TYPE_UI)); 102 (options.message_loop_type == MessageLoop::TYPE_UI));
87 #endif 103 #endif
88 104
89 SetThreadWasQuitProperly(false); 105 SetThreadWasQuitProperly(false);
90 106
91 MessageLoop::Type type = options.message_loop_type; 107 StartupData startup_data(options);
92 if (!options.message_pump_factory.is_null()) 108 startup_data_ = &startup_data;
93 type = MessageLoop::TYPE_CUSTOM;
94 109
95 message_loop_timer_slack_ = options.timer_slack; 110 if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
96 message_loop_ = new MessageLoop(type, options.message_pump_factory); 111 DLOG(ERROR) << "failed to create thread";
112 startup_data_ = NULL;
113 return false;
114 }
97 115
98 start_event_.reset(new WaitableEvent(false, false)); 116 // TODO(kinuko): Remove once crbug.com/465458 is solved.
117 tracked_objects::ScopedTracker tracking_profile_wait(
118 FROM_HERE_WITH_EXPLICIT_FUNCTION(
119 "465458 base::Thread::StartWithOptions (Wait)"));
99 120
100 // Hold the thread_lock_ while starting a new thread, so that we can make sure 121 // Wait for the thread to start and initialize message_loop_
101 // that thread_ is populated before the newly created thread accesses it. 122 base::ThreadRestrictions::ScopedAllowWait allow_wait;
102 { 123 startup_data.event.Wait();
103 AutoLock lock(thread_lock_); 124
104 if (!PlatformThread::Create(options.stack_size, this, &thread_)) { 125 // set it to NULL so we don't keep a pointer to some object on the stack.
105 DLOG(ERROR) << "failed to create thread"; 126 startup_data_ = NULL;
106 delete message_loop_; 127 started_ = true;
107 message_loop_ = nullptr;
108 start_event_.reset();
109 return false;
110 }
111 }
112 128
113 DCHECK(message_loop_); 129 DCHECK(message_loop_);
114 return true; 130 return true;
115 } 131 }
116 132
117 bool Thread::StartAndWaitForTesting() {
118 bool result = Start();
119 if (!result)
120 return false;
121 WaitUntilThreadStarted();
122 return true;
123 }
124
125 bool Thread::WaitUntilThreadStarted() {
126 if (!start_event_)
127 return false;
128 base::ThreadRestrictions::ScopedAllowWait allow_wait;
129 start_event_->Wait();
130 return true;
131 }
132
133 void Thread::Stop() { 133 void Thread::Stop() {
134 if (!start_event_) 134 if (!started_)
135 return; 135 return;
136 136
137 StopSoon(); 137 StopSoon();
138 138
139 // Wait for the thread to exit. 139 // Wait for the thread to exit.
140 // 140 //
141 // TODO(darin): Unfortunately, we need to keep message_loop_ around until 141 // TODO(darin): Unfortunately, we need to keep message_loop_ around until
142 // the thread exits. Some consumers are abusing the API. Make them stop. 142 // the thread exits. Some consumers are abusing the API. Make them stop.
143 // 143 //
144 PlatformThread::Join(thread_); 144 PlatformThread::Join(thread_);
145 145
146 // The thread should NULL message_loop_ on exit. 146 // The thread should NULL message_loop_ on exit.
147 DCHECK(!message_loop_); 147 DCHECK(!message_loop_);
148 148
149 // The thread no longer needs to be joined. 149 // The thread no longer needs to be joined.
150 start_event_.reset(); 150 started_ = false;
151 151
152 stopping_ = false; 152 stopping_ = false;
153 } 153 }
154 154
155 void Thread::StopSoon() { 155 void Thread::StopSoon() {
156 // We should only be called on the same thread that started us. 156 // We should only be called on the same thread that started us.
157 157
158 DCHECK_NE(thread_id(), PlatformThread::CurrentId()); 158 // Reading thread_id_ without a lock can lead to a benign data race
159 // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer.
160 DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId());
159 161
160 if (stopping_ || !message_loop_) 162 if (stopping_ || !message_loop_)
161 return; 163 return;
162 164
163 stopping_ = true; 165 stopping_ = true;
164 task_runner()->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); 166 task_runner()->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper));
165 } 167 }
166 168
167 PlatformThreadId Thread::thread_id() const {
168 AutoLock lock(thread_lock_);
169 return thread_.id();
170 }
171
172 bool Thread::IsRunning() const { 169 bool Thread::IsRunning() const {
173 // If the thread's already started (i.e. message_loop_ is non-null) and
174 // not yet requested to stop (i.e. stopping_ is false) we can just return
175 // true. (Note that stopping_ is touched only on the same thread that
176 // starts / started the new thread so we need no locking here.)
177 if (message_loop_ && !stopping_)
178 return true;
179 // Otherwise check the running_ flag, which is set to true by the new thread
180 // only while it is inside Run().
181 AutoLock lock(running_lock_);
182 return running_; 170 return running_;
183 } 171 }
184 172
185 void Thread::SetPriority(ThreadPriority priority) { 173 void Thread::SetPriority(ThreadPriority priority) {
186 // The thread must be started (and id known) for this to be 174 // The thread must be started (and id known) for this to be
187 // compatible with all platforms. 175 // compatible with all platforms.
188 DCHECK(message_loop_ != nullptr); 176 DCHECK_NE(thread_id_, kInvalidThreadId);
189 PlatformThread::SetThreadPriority(thread_, priority); 177 PlatformThread::SetThreadPriority(thread_, priority);
190 } 178 }
191 179
192 void Thread::Run(MessageLoop* message_loop) { 180 void Thread::Run(MessageLoop* message_loop) {
193 message_loop->Run(); 181 message_loop->Run();
194 } 182 }
195 183
196 void Thread::SetThreadWasQuitProperly(bool flag) { 184 void Thread::SetThreadWasQuitProperly(bool flag) {
197 lazy_tls_bool.Pointer()->Set(flag); 185 lazy_tls_bool.Pointer()->Set(flag);
198 } 186 }
199 187
200 bool Thread::GetThreadWasQuitProperly() { 188 bool Thread::GetThreadWasQuitProperly() {
201 bool quit_properly = true; 189 bool quit_properly = true;
202 #ifndef NDEBUG 190 #ifndef NDEBUG
203 quit_properly = lazy_tls_bool.Pointer()->Get(); 191 quit_properly = lazy_tls_bool.Pointer()->Get();
204 #endif 192 #endif
205 return quit_properly; 193 return quit_properly;
206 } 194 }
207 195
208 void Thread::ThreadMain() { 196 void Thread::ThreadMain() {
209 // Complete the initialization of our Thread object. 197 {
210 PlatformThread::SetName(name_.c_str()); 198 // The message loop for this thread.
211 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. 199 // Allocated on the heap to centralize any leak reports at this line.
200 scoped_ptr<MessageLoop> message_loop;
201 if (!startup_data_->options.message_pump_factory.is_null()) {
202 message_loop.reset(
203 new MessageLoop(startup_data_->options.message_pump_factory.Run()));
204 } else {
205 message_loop.reset(
206 new MessageLoop(startup_data_->options.message_loop_type));
207 }
212 208
213 // Lazily initialize the message_loop so that it can run on this thread. 209 // Complete the initialization of our Thread object.
214 DCHECK(message_loop_); 210 thread_id_ = PlatformThread::CurrentId();
215 scoped_ptr<MessageLoop> message_loop(message_loop_); 211 PlatformThread::SetName(name_);
216 message_loop_->BindToCurrentThread(); 212 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
217 message_loop_->set_thread_name(name_); 213 message_loop->set_thread_name(name_);
218 message_loop_->SetTimerSlack(message_loop_timer_slack_); 214 message_loop->SetTimerSlack(startup_data_->options.timer_slack);
215 message_loop_ = message_loop.get();
219 216
220 #if defined(OS_WIN) 217 #if defined(OS_WIN)
221 scoped_ptr<win::ScopedCOMInitializer> com_initializer; 218 scoped_ptr<win::ScopedCOMInitializer> com_initializer;
222 if (com_status_ != NONE) { 219 if (com_status_ != NONE) {
223 com_initializer.reset((com_status_ == STA) ? 220 com_initializer.reset((com_status_ == STA) ?
224 new win::ScopedCOMInitializer() : 221 new win::ScopedCOMInitializer() :
225 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); 222 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
226 } 223 }
227 #endif 224 #endif
228 225
229 // Make sure the thread_id() returns current thread. 226 // Let the thread do extra initialization.
230 // (This internally acquires lock against PlatformThread::Create) 227 // Let's do this before signaling we are started.
231 DCHECK_EQ(thread_id(), PlatformThread::CurrentId()); 228 Init();
232 229
233 // Let the thread do extra initialization. 230 running_ = true;
234 Init(); 231 startup_data_->event.Signal();
232 // startup_data_ can't be touched anymore since the starting thread is now
233 // unlocked.
235 234
236 { 235 Run(message_loop_);
237 AutoLock lock(running_lock_); 236 running_ = false;
238 running_ = true;
239 }
240 237
241 start_event_->Signal(); 238 // Let the thread do extra cleanup.
242 239 CleanUp();
243 Run(message_loop_);
244
245 {
246 AutoLock lock(running_lock_);
247 running_ = false;
248 }
249
250 // Let the thread do extra cleanup.
251 CleanUp();
252 240
253 #if defined(OS_WIN) 241 #if defined(OS_WIN)
254 com_initializer.reset(); 242 com_initializer.reset();
255 #endif 243 #endif
256 244
257 // Assert that MessageLoop::Quit was called by ThreadQuitHelper. 245 // Assert that MessageLoop::Quit was called by ThreadQuitHelper.
258 DCHECK(GetThreadWasQuitProperly()); 246 DCHECK(GetThreadWasQuitProperly());
259 247
260 // We can't receive messages anymore. 248 // We can't receive messages anymore.
261 // (The message loop is destructed at the end of this block) 249 message_loop_ = NULL;
262 message_loop_ = NULL; 250 }
263 } 251 }
264 252
265 } // namespace base 253 } // namespace base
OLDNEW
« no previous file with comments | « base/threading/thread.h ('k') | base/threading/thread_id_name_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698