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

Side by Side Diff: base/task_scheduler/scheduler_worker.cc

Issue 2692863012: SchedulerWorker Refcounting for Destruction in Production (Closed)
Patch Set: CR Feedback Created 3 years, 10 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/task_scheduler/scheduler_worker.h" 5 #include "base/task_scheduler/scheduler_worker.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <utility> 9 #include <utility>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/ptr_util.h" 12 #include "base/memory/ptr_util.h"
13 #include "base/task_scheduler/task_tracker.h" 13 #include "base/task_scheduler/task_tracker.h"
14 14
15 #if defined(OS_MACOSX) 15 #if defined(OS_MACOSX)
16 #include "base/mac/scoped_nsautorelease_pool.h" 16 #include "base/mac/scoped_nsautorelease_pool.h"
17 #elif defined(OS_WIN) 17 #elif defined(OS_WIN)
18 #include "base/win/scoped_com_initializer.h" 18 #include "base/win/scoped_com_initializer.h"
19 #endif 19 #endif
20 20
21 namespace base { 21 namespace base {
22 namespace internal { 22 namespace internal {
23 23
24 class SchedulerWorker::Thread : public PlatformThread::Delegate { 24 class SchedulerWorker::Thread : public PlatformThread::Delegate {
25 public: 25 public:
26 ~Thread() override = default; 26 ~Thread() override = default;
27 27
28 static std::unique_ptr<Thread> Create(SchedulerWorker* outer) { 28 static std::unique_ptr<Thread> Create(scoped_refptr<SchedulerWorker> outer) {
29 std::unique_ptr<Thread> thread(new Thread(outer)); 29 std::unique_ptr<Thread> thread(new Thread(std::move(outer)));
30 thread->Initialize(); 30 thread->Initialize();
31 if (thread->thread_handle_.is_null()) 31 if (thread->thread_handle_.is_null())
32 return nullptr; 32 return nullptr;
33 return thread; 33 return thread;
34 } 34 }
35 35
36 // PlatformThread::Delegate. 36 // PlatformThread::Delegate.
37 void ThreadMain() override { 37 void ThreadMain() override {
38 // Set if this thread was detached. 38 // Set if this thread was detached.
39 std::unique_ptr<Thread> detached_thread; 39 std::unique_ptr<Thread> detached_thread;
40 40
41 outer_->delegate_->OnMainEntry(outer_); 41 outer_->delegate_->OnMainEntry(outer_.get());
42 42
43 // A SchedulerWorker starts out waiting for work. 43 // A SchedulerWorker starts out waiting for work.
44 WaitForWork(); 44 WaitForWork();
45 45
46 #if defined(OS_WIN) 46 #if defined(OS_WIN)
47 std::unique_ptr<win::ScopedCOMInitializer> com_initializer; 47 std::unique_ptr<win::ScopedCOMInitializer> com_initializer;
48 if (outer_->backward_compatibility_ == 48 if (outer_->backward_compatibility_ ==
49 SchedulerBackwardCompatibility::INIT_COM_STA) { 49 SchedulerBackwardCompatibility::INIT_COM_STA) {
50 com_initializer = MakeUnique<win::ScopedCOMInitializer>(); 50 com_initializer = MakeUnique<win::ScopedCOMInitializer>();
51 } 51 }
52 #endif 52 #endif
53 53
54 while (!outer_->task_tracker_->IsShutdownComplete() && 54 while (!outer_->ShouldExit()) {
55 !outer_->should_exit_for_testing_.IsSet()) {
56 DCHECK(outer_); 55 DCHECK(outer_);
57 56
58 #if defined(OS_MACOSX) 57 #if defined(OS_MACOSX)
59 mac::ScopedNSAutoreleasePool autorelease_pool; 58 mac::ScopedNSAutoreleasePool autorelease_pool;
60 #endif 59 #endif
61 60
62 UpdateThreadPriority(GetDesiredThreadPriority()); 61 UpdateThreadPriority(GetDesiredThreadPriority());
63 62
64 // Get the sequence containing the next task to execute. 63 // Get the sequence containing the next task to execute.
65 scoped_refptr<Sequence> sequence = outer_->delegate_->GetWork(outer_); 64 scoped_refptr<Sequence> sequence =
65 outer_->delegate_->GetWork(outer_.get());
66 if (!sequence) { 66 if (!sequence) {
67 if (outer_->delegate_->CanDetach(outer_)) { 67 if (outer_->delegate_->CanDetach(outer_.get())) {
68 detached_thread = outer_->Detach(); 68 detached_thread = outer_->DetachThreadObject(DetachNotify::DELEGATE);
69 if (detached_thread) { 69 if (detached_thread) {
70 outer_ = nullptr; 70 outer_ = nullptr;
71 DCHECK_EQ(detached_thread.get(), this); 71 DCHECK_EQ(detached_thread.get(), this);
72 PlatformThread::Detach(thread_handle_); 72 PlatformThread::Detach(thread_handle_);
73 break; 73 break;
74 } 74 }
75 } 75 }
76 WaitForWork(); 76 WaitForWork();
77 continue; 77 continue;
78 } 78 }
(...skipping 20 matching lines...) Expand all
99 // sleep if WakeUp() is called while this SchedulerWorker is awake. 99 // sleep if WakeUp() is called while this SchedulerWorker is awake.
100 wake_up_event_.Reset(); 100 wake_up_event_.Reset();
101 } 101 }
102 102
103 // If a wake up is pending and we successfully detached, somehow |outer_| 103 // If a wake up is pending and we successfully detached, somehow |outer_|
104 // was able to signal us which means it probably thinks we're still alive. 104 // was able to signal us which means it probably thinks we're still alive.
105 // This is bad as it will cause the WakeUp to no-op and |outer_| will be 105 // This is bad as it will cause the WakeUp to no-op and |outer_| will be
106 // stuck forever. 106 // stuck forever.
107 DCHECK(!detached_thread || !IsWakeUpPending()) << 107 DCHECK(!detached_thread || !IsWakeUpPending()) <<
108 "This thread was detached and woken up at the same time."; 108 "This thread was detached and woken up at the same time.";
109
110 // This thread is generally responsible for cleaning itself up except when
111 // JoinForTesting() is called.
112 // We arrive here in the following cases:
113 // Thread Detachment Request:
114 // * |detached_thread| will not be nullptr.
115 // ShouldExit() returns true:
116 // * Shutdown: DetachThreadObject returns the thread object.
117 // * Cleanup: DetachThreadObject returns the thread object.
118 // * Join: DetachThreadObject returns nullptr. Join cleans up.
gab 2017/02/23 19:25:24 It's also possible to receive the thread object in
robliao 2017/02/23 21:04:22 Sure, and done.
119 if (!detached_thread)
120 detached_thread = outer_->DetachThreadObject(DetachNotify::SILENT);
109 } 121 }
110 122
111 void Join() { PlatformThread::Join(thread_handle_); } 123 void Join() { PlatformThread::Join(thread_handle_); }
112 124
113 void WakeUp() { wake_up_event_.Signal(); } 125 void WakeUp() { wake_up_event_.Signal(); }
114 126
115 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); } 127 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); }
116 128
117 private: 129 private:
118 Thread(SchedulerWorker* outer) 130 Thread(scoped_refptr<SchedulerWorker> outer)
119 : outer_(outer), 131 : outer_(std::move(outer)),
120 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL, 132 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL,
121 WaitableEvent::InitialState::NOT_SIGNALED), 133 WaitableEvent::InitialState::NOT_SIGNALED),
122 current_thread_priority_(GetDesiredThreadPriority()) { 134 current_thread_priority_(GetDesiredThreadPriority()) {
123 DCHECK(outer_); 135 DCHECK(outer_);
124 } 136 }
125 137
126 void Initialize() { 138 void Initialize() {
127 constexpr size_t kDefaultStackSize = 0; 139 constexpr size_t kDefaultStackSize = 0;
128 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_, 140 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_,
129 current_thread_priority_); 141 current_thread_priority_);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 void UpdateThreadPriority(ThreadPriority desired_thread_priority) { 180 void UpdateThreadPriority(ThreadPriority desired_thread_priority) {
169 if (desired_thread_priority == current_thread_priority_) 181 if (desired_thread_priority == current_thread_priority_)
170 return; 182 return;
171 183
172 PlatformThread::SetCurrentThreadPriority(desired_thread_priority); 184 PlatformThread::SetCurrentThreadPriority(desired_thread_priority);
173 current_thread_priority_ = desired_thread_priority; 185 current_thread_priority_ = desired_thread_priority;
174 } 186 }
175 187
176 PlatformThreadHandle thread_handle_; 188 PlatformThreadHandle thread_handle_;
177 189
178 SchedulerWorker* outer_; 190 scoped_refptr<SchedulerWorker> outer_;
179 191
180 // Event signaled to wake up this thread. 192 // Event signaled to wake up this thread.
181 WaitableEvent wake_up_event_; 193 WaitableEvent wake_up_event_;
182 194
183 // Current priority of this thread. May be different from 195 // Current priority of this thread. May be different from
184 // |outer_->priority_hint_|. 196 // |outer_->priority_hint_|.
185 ThreadPriority current_thread_priority_; 197 ThreadPriority current_thread_priority_;
186 198
187 DISALLOW_COPY_AND_ASSIGN(Thread); 199 DISALLOW_COPY_AND_ASSIGN(Thread);
188 }; 200 };
189 201
190 std::unique_ptr<SchedulerWorker> SchedulerWorker::Create( 202 scoped_refptr<SchedulerWorker> SchedulerWorker::Create(
191 ThreadPriority priority_hint, 203 ThreadPriority priority_hint,
192 std::unique_ptr<Delegate> delegate, 204 std::unique_ptr<Delegate> delegate,
193 TaskTracker* task_tracker, 205 TaskTracker* task_tracker,
194 InitialState initial_state, 206 InitialState initial_state,
195 SchedulerBackwardCompatibility backward_compatibility) { 207 SchedulerBackwardCompatibility backward_compatibility) {
196 auto worker = 208 scoped_refptr<SchedulerWorker> worker(
197 WrapUnique(new SchedulerWorker(priority_hint, std::move(delegate), 209 new SchedulerWorker(priority_hint, std::move(delegate), task_tracker,
198 task_tracker, backward_compatibility)); 210 backward_compatibility));
199 // Creation happens before any other thread can reference this one, so no 211 // Creation happens before any other thread can reference this one, so no
200 // synchronization is necessary. 212 // synchronization is necessary.
201 if (initial_state == SchedulerWorker::InitialState::ALIVE) { 213 if (initial_state == SchedulerWorker::InitialState::ALIVE) {
202 worker->CreateThread(); 214 worker->CreateThread();
203 if (!worker->thread_) { 215 if (!worker->thread_) {
204 return nullptr; 216 return nullptr;
205 } 217 }
206 } 218 }
207 219
208 return worker; 220 return worker;
209 } 221 }
210 222
211 SchedulerWorker::~SchedulerWorker() {
212 // It is unexpected for |thread_| to be alive and for SchedulerWorker to
213 // destroy since SchedulerWorker owns the delegate needed by |thread_|.
214 // For testing, this generally means JoinForTesting was not called.
215 DCHECK(!thread_);
216 }
217
218 void SchedulerWorker::WakeUp() { 223 void SchedulerWorker::WakeUp() {
219 AutoSchedulerLock auto_lock(thread_lock_); 224 AutoSchedulerLock auto_lock(thread_lock_);
220 225
221 DCHECK(!should_exit_for_testing_.IsSet()); 226 DCHECK(!join_called_for_testing_.IsSet());
222 227
223 if (!thread_) 228 if (!thread_)
224 CreateThreadAssertSynchronized(); 229 CreateThreadAssertSynchronized();
225 230
226 if (thread_) 231 if (thread_)
227 thread_->WakeUp(); 232 thread_->WakeUp();
228 } 233 }
229 234
230 void SchedulerWorker::JoinForTesting() { 235 void SchedulerWorker::JoinForTesting() {
231 DCHECK(!should_exit_for_testing_.IsSet()); 236 DCHECK(!join_called_for_testing_.IsSet());
232 should_exit_for_testing_.Set(); 237 join_called_for_testing_.Set();
233 238
234 std::unique_ptr<Thread> thread; 239 std::unique_ptr<Thread> thread;
235 240
236 { 241 {
237 AutoSchedulerLock auto_lock(thread_lock_); 242 AutoSchedulerLock auto_lock(thread_lock_);
238 243
239 if (thread_) { 244 if (thread_) {
240 // Make sure the thread is awake. It will see that 245 // Make sure the thread is awake. It will see that
241 // |should_exit_for_testing_| is set and exit shortly after. 246 // |join_called_for_testing_| is set and exit shortly after.
242 thread_->WakeUp(); 247 thread_->WakeUp();
243 thread = std::move(thread_); 248 thread = std::move(thread_);
244 } 249 }
245 } 250 }
246 251
247 if (thread) 252 if (thread)
248 thread->Join(); 253 thread->Join();
249 } 254 }
250 255
251 bool SchedulerWorker::ThreadAliveForTesting() const { 256 bool SchedulerWorker::ThreadAliveForTesting() const {
252 AutoSchedulerLock auto_lock(thread_lock_); 257 AutoSchedulerLock auto_lock(thread_lock_);
253 return !!thread_; 258 return !!thread_;
254 } 259 }
255 260
261 void SchedulerWorker::Cleanup() {
262 // |should_exit_| is synchronized with |thread_| for writes here so that we
263 // can maintain access to |thread_| for wakeup. Otherwise, the thread may take
264 // away |thread_| for destruction.
265 AutoSchedulerLock auto_lock(thread_lock_);
266 DCHECK(!should_exit_.IsSet());
267 if (thread_) {
268 should_exit_.Set();
269 thread_->WakeUp();
270 }
271 }
272
256 SchedulerWorker::SchedulerWorker( 273 SchedulerWorker::SchedulerWorker(
257 ThreadPriority priority_hint, 274 ThreadPriority priority_hint,
258 std::unique_ptr<Delegate> delegate, 275 std::unique_ptr<Delegate> delegate,
259 TaskTracker* task_tracker, 276 TaskTracker* task_tracker,
260 SchedulerBackwardCompatibility backward_compatibility) 277 SchedulerBackwardCompatibility backward_compatibility)
261 : priority_hint_(priority_hint), 278 : priority_hint_(priority_hint),
262 delegate_(std::move(delegate)), 279 delegate_(std::move(delegate)),
263 task_tracker_(task_tracker) 280 task_tracker_(task_tracker)
264 #if defined(OS_WIN) 281 #if defined(OS_WIN)
265 , 282 ,
266 backward_compatibility_(backward_compatibility) 283 backward_compatibility_(backward_compatibility)
267 #endif 284 #endif
268 { 285 {
269 DCHECK(delegate_); 286 DCHECK(delegate_);
270 DCHECK(task_tracker_); 287 DCHECK(task_tracker_);
271 } 288 }
272 289
273 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::Detach() { 290 SchedulerWorker::~SchedulerWorker() {
291 // It is unexpected for |thread_| to be alive and for SchedulerWorker to
292 // destroy since SchedulerWorker owns the delegate needed by |thread_|.
293 // For testing, this generally means JoinForTesting was not called.
294 DCHECK(!thread_);
295 }
296
297 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::DetachThreadObject(
298 DetachNotify detach_notify) {
274 AutoSchedulerLock auto_lock(thread_lock_); 299 AutoSchedulerLock auto_lock(thread_lock_);
275 300
276 // Do not detach if the thread is being joined. 301 // Do not detach if the thread is being joined.
277 if (!thread_) { 302 if (!thread_) {
278 DCHECK(should_exit_for_testing_.IsSet()); 303 DCHECK(join_called_for_testing_.IsSet());
279 return nullptr; 304 return nullptr;
280 } 305 }
281 306
282 // If a wakeup is pending, then a WakeUp() came in while we were deciding to 307 // If a wakeup is pending, then a WakeUp() came in while we were deciding to
283 // detach. This means we can't go away anymore since we would break the 308 // detach. This means we can't go away anymore since we would break the
284 // guarantee that we call GetWork() after a successful wakeup. 309 // guarantee that we call GetWork() after a successful wakeup.
285 if (thread_->IsWakeUpPending()) 310 if (thread_->IsWakeUpPending())
286 return nullptr; 311 return nullptr;
287 312
288 // Call OnDetach() within the scope of |thread_lock_| to prevent the delegate 313 if (detach_notify == DetachNotify::DELEGATE) {
289 // from being used concurrently from an old and a new thread. 314 // Call OnDetach() within the scope of |thread_lock_| to prevent the
290 delegate_->OnDetach(); 315 // delegate from being used concurrently from an old and a new thread.
316 delegate_->OnDetach();
317 }
291 318
292 return std::move(thread_); 319 return std::move(thread_);
293 } 320 }
294 321
295 void SchedulerWorker::CreateThread() { 322 void SchedulerWorker::CreateThread() {
296 thread_ = Thread::Create(this); 323 thread_ = Thread::Create(make_scoped_refptr(this));
297 } 324 }
298 325
299 void SchedulerWorker::CreateThreadAssertSynchronized() { 326 void SchedulerWorker::CreateThreadAssertSynchronized() {
300 thread_lock_.AssertAcquired(); 327 thread_lock_.AssertAcquired();
301 CreateThread(); 328 CreateThread();
302 } 329 }
303 330
331 bool SchedulerWorker::ShouldExit() {
332 return task_tracker_->IsShutdownComplete() ||
333 join_called_for_testing_.IsSet() || should_exit_.IsSet();
334 }
335
304 } // namespace internal 336 } // namespace internal
305 } // namespace base 337 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698