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

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: StartAndWait -> StartAndWaitForTesting Created 5 years, 8 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)
91 if (com_status_ == STA) 74 if (com_status_ == STA)
92 options.message_loop_type = MessageLoop::TYPE_UI; 75 options.message_loop_type = MessageLoop::TYPE_UI;
93 #endif 76 #endif
94 return StartWithOptions(options); 77 return StartWithOptions(options);
95 } 78 }
96 79
97 bool Thread::StartWithOptions(const Options& options) { 80 bool Thread::StartWithOptions(const Options& options) {
98 DCHECK(!message_loop_); 81 DCHECK(!message_loop_);
99 #if defined(OS_WIN) 82 #if defined(OS_WIN)
100 DCHECK((com_status_ != STA) || 83 DCHECK((com_status_ != STA) ||
101 (options.message_loop_type == MessageLoop::TYPE_UI)); 84 (options.message_loop_type == MessageLoop::TYPE_UI));
102 #endif 85 #endif
103 86
104 SetThreadWasQuitProperly(false); 87 SetThreadWasQuitProperly(false);
105 88
106 StartupData startup_data(options); 89 MessageLoop::InitOptions message_loop_options;
107 startup_data_ = &startup_data; 90 message_loop_options.message_loop_type = options.message_loop_type;
91 message_loop_options.message_pump_factory = options.message_pump_factory;
92 message_loop_options.timer_slack = options.timer_slack;
93 message_loop_ = new MessageLoop(message_loop_options);
94
95 start_event_.reset(new WaitableEvent(false, false));
Nico 2015/04/24 21:31:16 Move this below the next if, then you don't need t
kinuko 2015/04/27 16:36:05 I think we need to do this here since it'll be rac
108 96
109 if (!PlatformThread::Create(options.stack_size, this, &thread_)) { 97 if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
110 DLOG(ERROR) << "failed to create thread"; 98 DLOG(ERROR) << "failed to create thread";
111 startup_data_ = NULL; 99 delete message_loop_;
100 message_loop_ = nullptr;
101 start_event_.reset();
112 return false; 102 return false;
113 } 103 }
114 104
115 // TODO(kinuko): Remove once crbug.com/465458 is solved.
116 tracked_objects::ScopedTracker tracking_profile_wait(
117 FROM_HERE_WITH_EXPLICIT_FUNCTION(
118 "465458 base::Thread::StartWithOptions (Wait)"));
119
120 // Wait for the thread to start and initialize message_loop_
121 base::ThreadRestrictions::ScopedAllowWait allow_wait;
122 startup_data.event.Wait();
123
124 // set it to NULL so we don't keep a pointer to some object on the stack.
125 startup_data_ = NULL;
126 started_ = true;
127
128 DCHECK(message_loop_); 105 DCHECK(message_loop_);
129 return true; 106 return true;
130 } 107 }
131 108
109 bool Thread::StartAndWaitForTesting() {
110 bool result = Start();
111 if (!result)
112 return result;
Nico 2015/04/24 21:31:16 nit: `return false;` reads 0.2s faster
kinuko 2015/04/27 16:36:05 Done.
113 WaitUntilThreadStartedForTesting();
114 return result;
115 }
116
117 bool Thread::WaitUntilThreadStartedForTesting() {
118 if (!start_event_)
119 return false;
120 // TODO(kinuko): Remove once crbug.com/465458 is solved.
121 tracked_objects::ScopedTracker tracking_profile(
122 FROM_HERE_WITH_EXPLICIT_FUNCTION(
123 "465458 base::Thread::WaitUntilThreadStarted()"));
124 base::ThreadRestrictions::ScopedAllowWait allow_wait;
125 start_event_->Wait();
126 return true;
127 }
128
132 void Thread::Stop() { 129 void Thread::Stop() {
133 if (!started_) 130 if (!start_event_)
134 return; 131 return;
135 132
136 StopSoon(); 133 StopSoon();
137 134
138 // Wait for the thread to exit. 135 // Wait for the thread to exit.
139 // 136 //
140 // TODO(darin): Unfortunately, we need to keep message_loop_ around until 137 // TODO(darin): Unfortunately, we need to keep message_loop_ around until
141 // the thread exits. Some consumers are abusing the API. Make them stop. 138 // the thread exits. Some consumers are abusing the API. Make them stop.
142 // 139 //
143 PlatformThread::Join(thread_); 140 PlatformThread::Join(thread_);
144 141
145 // The thread should NULL message_loop_ on exit. 142 // The thread should NULL message_loop_ on exit.
146 DCHECK(!message_loop_); 143 DCHECK(!message_loop_);
147 144
148 // The thread no longer needs to be joined. 145 // The thread no longer needs to be joined.
149 started_ = false; 146 start_event_.reset();
150 147
151 stopping_ = false; 148 stopping_ = false;
152 } 149 }
153 150
154 void Thread::StopSoon() { 151 void Thread::StopSoon() {
155 // We should only be called on the same thread that started us. 152 // We should only be called on the same thread that started us.
156 153
157 // Reading thread_id_ without a lock can lead to a benign data race 154 DCHECK_NE(thread_id(), PlatformThread::CurrentId());
158 // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer.
159 DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId());
160 155
161 if (stopping_ || !message_loop_) 156 if (stopping_ || !message_loop_)
162 return; 157 return;
163 158
164 stopping_ = true; 159 stopping_ = true;
165 message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper)); 160 message_loop_->PostTask(FROM_HERE, base::Bind(&ThreadQuitHelper));
166 } 161 }
167 162
168 bool Thread::IsRunning() const { 163 bool Thread::IsRunning() const {
164 if (message_loop_ && !stopping_)
165 return true;
166 AutoLock lock(lock_);
Nico 2015/04/24 21:31:16 Why is this lock needed now when it wasn't before?
kinuko 2015/04/27 16:36:05 Actually I think we should have had this lock even
Nico 2015/04/27 22:49:34 Maybe add a comment that says why accessing messag
kinuko 2015/04/28 15:43:25 Done.
169 return running_; 167 return running_;
170 } 168 }
171 169
172 void Thread::SetPriority(ThreadPriority priority) { 170 void Thread::SetPriority(ThreadPriority priority) {
173 // The thread must be started (and id known) for this to be 171 // The thread must be started (and id known) for this to be
174 // compatible with all platforms. 172 // compatible with all platforms.
175 DCHECK_NE(thread_id_, kInvalidThreadId); 173 DCHECK(message_loop_ != nullptr);
176 PlatformThread::SetThreadPriority(thread_, priority); 174 PlatformThread::SetThreadPriority(thread_, priority);
177 } 175 }
178 176
179 void Thread::Run(MessageLoop* message_loop) { 177 void Thread::Run(MessageLoop* message_loop) {
180 message_loop->Run(); 178 message_loop->Run();
181 } 179 }
182 180
183 void Thread::SetThreadWasQuitProperly(bool flag) { 181 void Thread::SetThreadWasQuitProperly(bool flag) {
184 lazy_tls_bool.Pointer()->Set(flag); 182 lazy_tls_bool.Pointer()->Set(flag);
185 } 183 }
186 184
187 bool Thread::GetThreadWasQuitProperly() { 185 bool Thread::GetThreadWasQuitProperly() {
188 bool quit_properly = true; 186 bool quit_properly = true;
189 #ifndef NDEBUG 187 #ifndef NDEBUG
190 quit_properly = lazy_tls_bool.Pointer()->Get(); 188 quit_properly = lazy_tls_bool.Pointer()->Get();
191 #endif 189 #endif
192 return quit_properly; 190 return quit_properly;
193 } 191 }
194 192
195 void Thread::ThreadMain() { 193 void Thread::ThreadMain() {
196 { 194 // Complete the initialization of our Thread object.
197 // The message loop for this thread. 195 DCHECK_EQ(thread_id(), PlatformThread::CurrentId());
198 // Allocated on the heap to centralize any leak reports at this line. 196 PlatformThread::SetName(name_.c_str());
199 scoped_ptr<MessageLoop> message_loop; 197 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector.
200 if (!startup_data_->options.message_pump_factory.is_null()) {
201 message_loop.reset(
202 new MessageLoop(startup_data_->options.message_pump_factory.Run()));
203 } else {
204 message_loop.reset(
205 new MessageLoop(startup_data_->options.message_loop_type));
206 }
207 198
208 // Complete the initialization of our Thread object. 199 // Lazily initialize the message_loop so that it can run on this thread.
209 thread_id_ = PlatformThread::CurrentId(); 200 DCHECK(message_loop_);
210 PlatformThread::SetName(name_.c_str()); 201 scoped_ptr<MessageLoop> message_loop(message_loop_);
211 ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. 202 message_loop_->set_thread_name(name_);
212 message_loop->set_thread_name(name_); 203 message_loop_->Init();
213 message_loop->SetTimerSlack(startup_data_->options.timer_slack);
214 message_loop_ = message_loop.get();
215 204
216 #if defined(OS_WIN) 205 #if defined(OS_WIN)
217 scoped_ptr<win::ScopedCOMInitializer> com_initializer; 206 scoped_ptr<win::ScopedCOMInitializer> com_initializer;
218 if (com_status_ != NONE) { 207 if (com_status_ != NONE) {
219 com_initializer.reset((com_status_ == STA) ? 208 com_initializer.reset((com_status_ == STA) ?
220 new win::ScopedCOMInitializer() : 209 new win::ScopedCOMInitializer() :
221 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA)); 210 new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
222 } 211 }
223 #endif 212 #endif
224 213
225 // Let the thread do extra initialization. 214 // Let the thread do extra initialization.
226 // Let's do this before signaling we are started. 215 Init();
227 Init();
228 216
217 {
218 AutoLock lock(lock_);
229 running_ = true; 219 running_ = true;
230 startup_data_->event.Signal(); 220 }
231 // startup_data_ can't be touched anymore since the starting thread is now
232 // unlocked.
233 221
234 Run(message_loop_); 222 start_event_->Signal();
223
224 Run(message_loop_);
225
226 {
227 AutoLock lock(lock_);
235 running_ = false; 228 running_ = false;
229 }
236 230
237 // Let the thread do extra cleanup. 231 // Let the thread do extra cleanup.
238 CleanUp(); 232 CleanUp();
239 233
240 #if defined(OS_WIN) 234 #if defined(OS_WIN)
241 com_initializer.reset(); 235 com_initializer.reset();
242 #endif 236 #endif
243 237
244 // Assert that MessageLoop::Quit was called by ThreadQuitHelper. 238 // Assert that MessageLoop::Quit was called by ThreadQuitHelper.
245 DCHECK(GetThreadWasQuitProperly()); 239 DCHECK(GetThreadWasQuitProperly());
246 240
247 // We can't receive messages anymore. 241 // We can't receive messages anymore.
248 message_loop_ = NULL; 242 // (The message loop is destructed at the end of this block)
249 } 243 message_loop_ = NULL;
250 } 244 }
251 245
252 } // namespace base 246 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698