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

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

Issue 1011683002: Lazily initialize MessageLoop for faster thread startup (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: non-blocking thread_id() Created 5 years, 9 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
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/profiler/scoped_tracker.h" 9 #include "base/profiler/scoped_tracker.h"
10 #include "base/synchronization/waitable_event.h" 10 #include "base/synchronization/waitable_event.h"
(...skipping 18 matching lines...) Expand all
29 LAZY_INSTANCE_INITIALIZER; 29 LAZY_INSTANCE_INITIALIZER;
30 30
31 } // namespace 31 } // namespace
32 32
33 // This is used to trigger the message loop to exit. 33 // This is used to trigger the message loop to exit.
34 void ThreadQuitHelper() { 34 void ThreadQuitHelper() {
35 MessageLoop::current()->QuitWhenIdle(); 35 MessageLoop::current()->QuitWhenIdle();
36 Thread::SetThreadWasQuitProperly(true); 36 Thread::SetThreadWasQuitProperly(true);
37 } 37 }
38 38
39 // Used to pass data to ThreadMain. This structure is allocated on the stack
40 // from within StartWithOptions.
41 struct Thread::StartupData {
42 // We get away with a const reference here because of how we are allocated.
43 const Thread::Options& options;
44
45 // Used to synchronize thread startup.
46 WaitableEvent event;
47
48 explicit StartupData(const Options& opt)
49 : options(opt),
50 event(false, false) {}
51 };
52
53 Thread::Options::Options() 39 Thread::Options::Options()
54 : message_loop_type(MessageLoop::TYPE_DEFAULT), 40 : message_loop_type(MessageLoop::TYPE_DEFAULT),
55 timer_slack(TIMER_SLACK_NONE), 41 timer_slack(TIMER_SLACK_NONE),
56 stack_size(0) { 42 stack_size(0) {
57 } 43 }
58 44
59 Thread::Options::Options(MessageLoop::Type type, 45 Thread::Options::Options(MessageLoop::Type type,
60 size_t size) 46 size_t size)
61 : message_loop_type(type), 47 : message_loop_type(type),
62 timer_slack(TIMER_SLACK_NONE), 48 timer_slack(TIMER_SLACK_NONE),
63 stack_size(size) { 49 stack_size(size) {
64 } 50 }
65 51
66 Thread::Options::~Options() { 52 Thread::Options::~Options() {
67 } 53 }
68 54
69 Thread::Thread(const std::string& name) 55 Thread::Thread(const std::string& name)
70 : 56 :
71 #if defined(OS_WIN) 57 #if defined(OS_WIN)
72 com_status_(NONE), 58 com_status_(NONE),
73 #endif 59 #endif
74 started_(false),
75 stopping_(false), 60 stopping_(false),
76 running_(false), 61 running_(false),
77 startup_data_(NULL),
78 thread_(0), 62 thread_(0),
79 message_loop_(NULL), 63 message_loop_(nullptr),
80 thread_id_(kInvalidThreadId),
81 name_(name) { 64 name_(name) {
82 } 65 }
83 66
84 Thread::~Thread() { 67 Thread::~Thread() {
85 Stop(); 68 Stop();
86 } 69 }
87 70
88 bool Thread::Start() { 71 bool Thread::Start() {
89 Options options; 72 Options options;
90 #if defined(OS_WIN) 73 #if defined(OS_WIN)
(...skipping 10 matching lines...) Expand all
101 "465458 base::Thread::StartWithOptions")); 84 "465458 base::Thread::StartWithOptions"));
102 85
103 DCHECK(!message_loop_); 86 DCHECK(!message_loop_);
104 #if defined(OS_WIN) 87 #if defined(OS_WIN)
105 DCHECK((com_status_ != STA) || 88 DCHECK((com_status_ != STA) ||
106 (options.message_loop_type == MessageLoop::TYPE_UI)); 89 (options.message_loop_type == MessageLoop::TYPE_UI));
107 #endif 90 #endif
108 91
109 SetThreadWasQuitProperly(false); 92 SetThreadWasQuitProperly(false);
110 93
111 StartupData startup_data(options); 94 scoped_ptr<MessageLoop::LazyInitOptions> message_loop_options(
112 startup_data_ = &startup_data; 95 new MessageLoop::LazyInitOptions);
96 message_loop_options->message_loop_type = options.message_loop_type;
97 message_loop_options->message_pump_factory = options.message_pump_factory;
98 message_loop_options->timer_slack = options.timer_slack;
99 message_loop_ = MessageLoop::CreateForLazyInit(message_loop_options.Pass());
100
101 start_event_.reset(new WaitableEvent(false, false));
113 102
114 if (!PlatformThread::Create(options.stack_size, this, &thread_)) { 103 if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
115 DLOG(ERROR) << "failed to create thread"; 104 DLOG(ERROR) << "failed to create thread";
116 startup_data_ = NULL; 105 delete message_loop_;
106 message_loop_ = NULL;
107 start_event_.reset();
117 return false; 108 return false;
118 } 109 }
119 110
120 // TODO(eroman): Remove once crbug.com/465458 is solved.
121 tracked_objects::ScopedTracker tracking_profile_wait(
122 FROM_HERE_WITH_EXPLICIT_FUNCTION(
123 "465458 base::Thread::StartWithOptions (Wait)"));
124
125 // Wait for the thread to start and initialize message_loop_
126 base::ThreadRestrictions::ScopedAllowWait allow_wait;
127 startup_data.event.Wait();
128
129 // set it to NULL so we don't keep a pointer to some object on the stack.
130 startup_data_ = NULL;
131 started_ = true;
132
133 DCHECK(message_loop_); 111 DCHECK(message_loop_);
134 return true; 112 return true;
135 } 113 }
136 114
115 bool Thread::StartAndWait() {
116 bool result = Start();
117 if (!result)
118 return result;
119 WaitUntilThreadStarted();
120 return result;
121 }
122
123 bool Thread::WaitUntilThreadStarted() {
124 if (!start_event_)
125 return false;
126 // TODO(kinuko): Remove once crbug.com/465458 is solved.
127 tracked_objects::ScopedTracker tracking_profile(
128 FROM_HERE_WITH_EXPLICIT_FUNCTION(
129 "465458 base::Thread::WaitUntilThreadStarted()"));
130 base::ThreadRestrictions::ScopedAllowWait allow_wait;
131 start_event_->Wait();
132 return true;
133 }
134
137 void Thread::Stop() { 135 void Thread::Stop() {
138 if (!started_) 136 if (!start_event_)
139 return; 137 return;
140 138
141 StopSoon(); 139 StopSoon();
142 140
143 // Wait for the thread to exit. 141 // Wait for the thread to exit.
144 // 142 //
145 // TODO(darin): Unfortunately, we need to keep message_loop_ around until 143 // TODO(darin): Unfortunately, we need to keep message_loop_ around until
146 // the thread exits. Some consumers are abusing the API. Make them stop. 144 // the thread exits. Some consumers are abusing the API. Make them stop.
147 // 145 //
148 PlatformThread::Join(thread_); 146 PlatformThread::Join(thread_);
149 147
150 // The thread should NULL message_loop_ on exit. 148 // The thread should NULL message_loop_ on exit.
151 DCHECK(!message_loop_); 149 DCHECK(!message_loop_);
152 150
153 // The thread no longer needs to be joined. 151 // The thread no longer needs to be joined.
154 started_ = false; 152 start_event_.reset();
155 153
156 stopping_ = false; 154 stopping_ = false;
157 } 155 }
158 156
159 void Thread::StopSoon() { 157 void Thread::StopSoon() {
160 // We should only be called on the same thread that started us. 158 // We should only be called on the same thread that started us.
161 159
162 // Reading thread_id_ without a lock can lead to a benign data race 160 DCHECK_NE(thread_id(), PlatformThread::CurrentId());
163 // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer.
164 DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId());
165 161
166 if (stopping_ || !message_loop_) 162 if (stopping_ || !message_loop_)
167 return; 163 return;
168 164
169 stopping_ = true; 165 stopping_ = true;
170 message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); 166 message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper));
171 } 167 }
172 168
173 bool Thread::IsRunning() const { 169 bool Thread::IsRunning() const {
174 return running_; 170 if (stopping_)
rvargas (doing something else) 2015/03/26 02:19:27 Now I see... there's a giant lock in Stop (Join),
kinuko 2015/04/13 02:02:59 Not only for testing but also in production code (
171 return IsInRunLoop();
172 return !!start_event_;
175 } 173 }
176 174
177 void Thread::SetPriority(ThreadPriority priority) { 175 void Thread::SetPriority(ThreadPriority priority) {
178 // The thread must be started (and id known) for this to be 176 // The thread must be started (and id known) for this to be
179 // compatible with all platforms. 177 // compatible with all platforms.
180 DCHECK_NE(thread_id_, kInvalidThreadId); 178 DCHECK(!!start_event_);
rvargas (doing something else) 2015/03/26 02:19:27 nit: shouldn't need !! (scoped_ptr is testable)
kinuko 2015/04/13 02:02:59 Done.
181 PlatformThread::SetThreadPriority(thread_, priority); 179 PlatformThread::SetThreadPriority(thread_, priority);
182 } 180 }
183 181
184 void Thread::Run(MessageLoop* message_loop) { 182 void Thread::Run(MessageLoop* message_loop) {
185 message_loop->Run(); 183 message_loop->Run();
186 } 184 }
187 185
188 void Thread::SetThreadWasQuitProperly(bool flag) { 186 void Thread::SetThreadWasQuitProperly(bool flag) {
189 lazy_tls_bool.Pointer()->Set(flag); 187 lazy_tls_bool.Pointer()->Set(flag);
190 } 188 }
191 189
192 bool Thread::GetThreadWasQuitProperly() { 190 bool Thread::GetThreadWasQuitProperly() {
193 bool quit_properly = true; 191 bool quit_properly = true;
194 #ifndef NDEBUG 192 #ifndef NDEBUG
195 quit_properly = lazy_tls_bool.Pointer()->Get(); 193 quit_properly = lazy_tls_bool.Pointer()->Get();
196 #endif 194 #endif
197 return quit_properly; 195 return quit_properly;
198 } 196 }
199 197
200 void Thread::ThreadMain() { 198 void Thread::ThreadMain() {
201 { 199 {
rvargas (doing something else) 2015/03/26 02:19:27 nit: remove this
kinuko 2015/04/13 02:03:00 Done.
202 // The message loop for this thread.
203 // Allocated on the heap to centralize any leak reports at this line.
204 scoped_ptr<MessageLoop> message_loop;
205 if (!startup_data_->options.message_pump_factory.is_null()) {
206 message_loop.reset(
207 new MessageLoop(startup_data_->options.message_pump_factory.Run()));
208 } else {
209 message_loop.reset(
210 new MessageLoop(startup_data_->options.message_loop_type));
211 }
212
213 // Complete the initialization of our Thread object. 200 // Complete the initialization of our Thread object.
214 thread_id_ = PlatformThread::CurrentId(); 201 DCHECK_EQ(thread_id(), PlatformThread::CurrentId());
215 PlatformThread::SetName(name_.c_str()); 202 PlatformThread::SetName(name_.c_str());
216 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. 203 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
217 message_loop->set_thread_name(name_); 204
218 message_loop->SetTimerSlack(startup_data_->options.timer_slack); 205 // Lazily initialize the message_loop so that it can run on this thread.
219 message_loop_ = message_loop.get(); 206 DCHECK(message_loop_);
207 scoped_ptr<MessageLoop> message_loop(message_loop_);
rvargas (doing something else) 2015/03/26 02:19:28 Is this intentional?
kinuko 2015/04/13 02:03:00 Sort of. (This needs to be freed at the end of mai
208 message_loop_->set_thread_name(name_);
209 message_loop_->LazyInit();
220 210
221 #if defined(OS_WIN) 211 #if defined(OS_WIN)
222 scoped_ptr<win::ScopedCOMInitializer> com_initializer; 212 scoped_ptr<win::ScopedCOMInitializer> com_initializer;
223 if (com_status_ != NONE) { 213 if (com_status_ != NONE) {
224 com_initializer.reset((com_status_ == STA) ? 214 com_initializer.reset((com_status_ == STA) ?
225 new win::ScopedCOMInitializer() : 215 new win::ScopedCOMInitializer() :
226 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); 216 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
227 } 217 }
228 #endif 218 #endif
229 219
230 // Let the thread do extra initialization. 220 // Let the thread do extra initialization.
231 // Let's do this before signaling we are started.
232 Init(); 221 Init();
233 222
234 running_ = true; 223 {
235 startup_data_->event.Signal(); 224 AutoLock lock(lock_);
236 // startup_data_ can't be touched anymore since the starting thread is now 225 running_ = true;
237 // unlocked. 226 }
227
228 start_event_->Signal();
238 229
239 Run(message_loop_); 230 Run(message_loop_);
240 running_ = false; 231
232 {
233 AutoLock lock(lock_);
234 running_ = false;
235 }
241 236
242 // Let the thread do extra cleanup. 237 // Let the thread do extra cleanup.
243 CleanUp(); 238 CleanUp();
244 239
245 #if defined(OS_WIN) 240 #if defined(OS_WIN)
246 com_initializer.reset(); 241 com_initializer.reset();
247 #endif 242 #endif
248 243
249 // Assert that MessageLoop::Quit was called by ThreadQuitHelper. 244 // Assert that MessageLoop::Quit was called by ThreadQuitHelper.
250 DCHECK(GetThreadWasQuitProperly()); 245 DCHECK(GetThreadWasQuitProperly());
251 246
252 // We can't receive messages anymore. 247 // We can't receive messages anymore.
253 message_loop_ = NULL; 248 message_loop_ = NULL;
254 } 249 }
255 } 250 }
256 251
252 bool Thread::IsInRunLoop() const {
253 base::AutoLock lock(lock_);
254 return running_;
255 }
256
257 } // namespace base 257 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698