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

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

Issue 2692863012: SchedulerWorker Refcounting for Destruction in Production (Closed)
Patch Set: Fix Up Comments and Description 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 // To maintain the condition that the thread always cleans itself up except
111 // in Join(), we go ahead and grab the thread object here. We don't want to
gab 2017/02/21 19:01:04 What makes Join() not hit this code?
robliao 2017/02/21 22:26:57 Join() nulls out |thread_| which means DetachThrea
gab 2017/02/22 18:02:23 I see and JoinForTesting() also checks for null |t
robliao 2017/02/22 20:43:39 I've enumerated the conditions, but we should keep
112 // notify the delegate since it's not a detachment.
113 if (!detached_thread)
114 detached_thread = outer_->DetachThreadObject(DetachNotify::SILENT);
109 } 115 }
110 116
111 void Join() { PlatformThread::Join(thread_handle_); } 117 void Join() { PlatformThread::Join(thread_handle_); }
112 118
113 void WakeUp() { wake_up_event_.Signal(); } 119 void WakeUp() { wake_up_event_.Signal(); }
114 120
115 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); } 121 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); }
116 122
117 private: 123 private:
118 Thread(SchedulerWorker* outer) 124 Thread(scoped_refptr<SchedulerWorker> outer)
119 : outer_(outer), 125 : outer_(std::move(outer)),
120 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL, 126 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL,
121 WaitableEvent::InitialState::NOT_SIGNALED), 127 WaitableEvent::InitialState::NOT_SIGNALED),
122 current_thread_priority_(GetDesiredThreadPriority()) { 128 current_thread_priority_(GetDesiredThreadPriority()) {
123 DCHECK(outer_); 129 DCHECK(outer_);
124 } 130 }
125 131
126 void Initialize() { 132 void Initialize() {
127 constexpr size_t kDefaultStackSize = 0; 133 constexpr size_t kDefaultStackSize = 0;
128 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_, 134 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_,
129 current_thread_priority_); 135 current_thread_priority_);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 void UpdateThreadPriority(ThreadPriority desired_thread_priority) { 174 void UpdateThreadPriority(ThreadPriority desired_thread_priority) {
169 if (desired_thread_priority == current_thread_priority_) 175 if (desired_thread_priority == current_thread_priority_)
170 return; 176 return;
171 177
172 PlatformThread::SetCurrentThreadPriority(desired_thread_priority); 178 PlatformThread::SetCurrentThreadPriority(desired_thread_priority);
173 current_thread_priority_ = desired_thread_priority; 179 current_thread_priority_ = desired_thread_priority;
174 } 180 }
175 181
176 PlatformThreadHandle thread_handle_; 182 PlatformThreadHandle thread_handle_;
177 183
178 SchedulerWorker* outer_; 184 scoped_refptr<SchedulerWorker> outer_;
179 185
180 // Event signaled to wake up this thread. 186 // Event signaled to wake up this thread.
181 WaitableEvent wake_up_event_; 187 WaitableEvent wake_up_event_;
182 188
183 // Current priority of this thread. May be different from 189 // Current priority of this thread. May be different from
184 // |outer_->priority_hint_|. 190 // |outer_->priority_hint_|.
185 ThreadPriority current_thread_priority_; 191 ThreadPriority current_thread_priority_;
186 192
187 DISALLOW_COPY_AND_ASSIGN(Thread); 193 DISALLOW_COPY_AND_ASSIGN(Thread);
188 }; 194 };
189 195
190 std::unique_ptr<SchedulerWorker> SchedulerWorker::Create( 196 scoped_refptr<SchedulerWorker> SchedulerWorker::Create(
191 ThreadPriority priority_hint, 197 ThreadPriority priority_hint,
192 std::unique_ptr<Delegate> delegate, 198 std::unique_ptr<Delegate> delegate,
193 TaskTracker* task_tracker, 199 TaskTracker* task_tracker,
194 InitialState initial_state, 200 InitialState initial_state,
195 SchedulerBackwardCompatibility backward_compatibility) { 201 SchedulerBackwardCompatibility backward_compatibility) {
196 auto worker = 202 scoped_refptr<SchedulerWorker> worker(
197 WrapUnique(new SchedulerWorker(priority_hint, std::move(delegate), 203 new SchedulerWorker(priority_hint, std::move(delegate), task_tracker,
198 task_tracker, backward_compatibility)); 204 backward_compatibility));
199 // Creation happens before any other thread can reference this one, so no 205 // Creation happens before any other thread can reference this one, so no
200 // synchronization is necessary. 206 // synchronization is necessary.
201 if (initial_state == SchedulerWorker::InitialState::ALIVE) { 207 if (initial_state == SchedulerWorker::InitialState::ALIVE) {
202 worker->CreateThread(); 208 worker->CreateThread();
203 if (!worker->thread_) { 209 if (!worker->thread_) {
204 return nullptr; 210 return nullptr;
205 } 211 }
206 } 212 }
207 213
208 return worker; 214 return worker;
209 } 215 }
210 216
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() { 217 void SchedulerWorker::WakeUp() {
219 AutoSchedulerLock auto_lock(thread_lock_); 218 AutoSchedulerLock auto_lock(thread_lock_);
220 219
221 DCHECK(!should_exit_for_testing_.IsSet()); 220 DCHECK(!join_called_for_testing_.IsSet());
222 221
223 if (!thread_) 222 if (!thread_)
224 CreateThreadAssertSynchronized(); 223 CreateThreadAssertSynchronized();
225 224
226 if (thread_) 225 if (thread_)
227 thread_->WakeUp(); 226 thread_->WakeUp();
228 } 227 }
229 228
230 void SchedulerWorker::JoinForTesting() { 229 void SchedulerWorker::JoinForTesting() {
231 DCHECK(!should_exit_for_testing_.IsSet()); 230 DCHECK(!join_called_for_testing_.IsSet());
232 should_exit_for_testing_.Set(); 231 join_called_for_testing_.Set();
233 232
234 std::unique_ptr<Thread> thread; 233 std::unique_ptr<Thread> thread;
235 234
236 { 235 {
237 AutoSchedulerLock auto_lock(thread_lock_); 236 AutoSchedulerLock auto_lock(thread_lock_);
238 237
239 if (thread_) { 238 if (thread_) {
240 // Make sure the thread is awake. It will see that 239 // Make sure the thread is awake. It will see that
241 // |should_exit_for_testing_| is set and exit shortly after. 240 // |join_called_for_testing_| is set and exit shortly after.
242 thread_->WakeUp(); 241 thread_->WakeUp();
243 thread = std::move(thread_); 242 thread = std::move(thread_);
244 } 243 }
245 } 244 }
246 245
247 if (thread) 246 if (thread)
248 thread->Join(); 247 thread->Join();
249 } 248 }
250 249
251 bool SchedulerWorker::ThreadAliveForTesting() const { 250 bool SchedulerWorker::ThreadAliveForTesting() const {
252 AutoSchedulerLock auto_lock(thread_lock_); 251 AutoSchedulerLock auto_lock(thread_lock_);
253 return !!thread_; 252 return !!thread_;
254 } 253 }
255 254
255 void SchedulerWorker::Cleanup() {
256 // should_exit_ is synchronized with thread_ for writes here so that we can
gab 2017/02/21 19:01:04 |should_exit_| |thread_|
robliao 2017/02/21 22:26:57 Done.
257 // maintain access |thread_| for wakeup. Otherwise, the thread may take away
gab 2017/02/21 19:01:04 "maintain alive" or just "access" ?
robliao 2017/02/21 22:26:57 Lost a preposition there. Added a "to" -> maintain
258 // |thread_| for destruction.
259 AutoSchedulerLock auto_lock(thread_lock_);
260 DCHECK(!should_exit_.IsSet());
261 if (thread_) {
262 should_exit_.Set();
263 thread_->WakeUp();
264 }
265 }
266
256 SchedulerWorker::SchedulerWorker( 267 SchedulerWorker::SchedulerWorker(
257 ThreadPriority priority_hint, 268 ThreadPriority priority_hint,
258 std::unique_ptr<Delegate> delegate, 269 std::unique_ptr<Delegate> delegate,
259 TaskTracker* task_tracker, 270 TaskTracker* task_tracker,
260 SchedulerBackwardCompatibility backward_compatibility) 271 SchedulerBackwardCompatibility backward_compatibility)
261 : priority_hint_(priority_hint), 272 : priority_hint_(priority_hint),
262 delegate_(std::move(delegate)), 273 delegate_(std::move(delegate)),
263 task_tracker_(task_tracker) 274 task_tracker_(task_tracker)
264 #if defined(OS_WIN) 275 #if defined(OS_WIN)
265 , 276 ,
266 backward_compatibility_(backward_compatibility) 277 backward_compatibility_(backward_compatibility)
267 #endif 278 #endif
268 { 279 {
269 DCHECK(delegate_); 280 DCHECK(delegate_);
270 DCHECK(task_tracker_); 281 DCHECK(task_tracker_);
271 } 282 }
272 283
273 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::Detach() { 284 SchedulerWorker::~SchedulerWorker() {
285 // It is unexpected for |thread_| to be alive and for SchedulerWorker to
286 // destroy since SchedulerWorker owns the delegate needed by |thread_|.
287 // For testing, this generally means JoinForTesting was not called.
288 DCHECK(!thread_);
289 }
290
291 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::DetachThreadObject(
292 DetachNotify detach_notify) {
274 AutoSchedulerLock auto_lock(thread_lock_); 293 AutoSchedulerLock auto_lock(thread_lock_);
275 294
276 // Do not detach if the thread is being joined. 295 // Do not detach if the thread is being joined.
277 if (!thread_) { 296 if (!thread_) {
278 DCHECK(should_exit_for_testing_.IsSet()); 297 DCHECK(join_called_for_testing_.IsSet());
279 return nullptr; 298 return nullptr;
280 } 299 }
281 300
282 // If a wakeup is pending, then a WakeUp() came in while we were deciding to 301 // 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 302 // detach. This means we can't go away anymore since we would break the
284 // guarantee that we call GetWork() after a successful wakeup. 303 // guarantee that we call GetWork() after a successful wakeup.
285 if (thread_->IsWakeUpPending()) 304 if (thread_->IsWakeUpPending())
286 return nullptr; 305 return nullptr;
287 306
288 // Call OnDetach() within the scope of |thread_lock_| to prevent the delegate 307 if (detach_notify == DetachNotify::DELEGATE) {
289 // from being used concurrently from an old and a new thread. 308 // Call OnDetach() within the scope of |thread_lock_| to prevent the
290 delegate_->OnDetach(); 309 // delegate from being used concurrently from an old and a new thread.
310 delegate_->OnDetach();
311 }
291 312
292 return std::move(thread_); 313 return std::move(thread_);
293 } 314 }
294 315
295 void SchedulerWorker::CreateThread() { 316 void SchedulerWorker::CreateThread() {
296 thread_ = Thread::Create(this); 317 thread_ = Thread::Create(this);
gab 2017/02/21 19:01:04 Thread::Create(scoped_refptr<SchedulerWorker>(this
robliao 2017/02/21 22:26:57 Went with make_scoped_refptr
297 } 318 }
298 319
299 void SchedulerWorker::CreateThreadAssertSynchronized() { 320 void SchedulerWorker::CreateThreadAssertSynchronized() {
300 thread_lock_.AssertAcquired(); 321 thread_lock_.AssertAcquired();
301 CreateThread(); 322 CreateThread();
302 } 323 }
303 324
325 bool SchedulerWorker::ShouldExit() {
326 bool should_exit =
327 task_tracker_->IsShutdownComplete() || join_called_for_testing_.IsSet();
gab 2017/02/22 18:02:24 inline below
robliao 2017/02/22 20:43:39 Done.
328 if (should_exit)
329 return true;
330
331 // We don't need to acquire |thread_lock_| here as the thread will acquire it
332 // on its own to perform changes to thread_.
gab 2017/02/22 18:02:24 |thread_|
robliao 2017/02/22 20:43:39 Removed
333 return should_exit_.IsSet();
gab 2017/02/22 18:02:23 It's unclear why this is checked separately now (i
robliao 2017/02/22 20:43:39 Done.
334 }
335
304 } // namespace internal 336 } // namespace internal
305 } // namespace base 337 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698