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

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 if (!detached_thread)
gab 2017/02/17 21:38:35 Add comment
robliao 2017/02/17 22:04:30 Done.
111 detached_thread = outer_->DetachThreadObject(DetachNotify::SILENT);
109 } 112 }
110 113
111 void Join() { PlatformThread::Join(thread_handle_); } 114 void Join() { PlatformThread::Join(thread_handle_); }
112 115
113 void WakeUp() { wake_up_event_.Signal(); } 116 void WakeUp() { wake_up_event_.Signal(); }
114 117
115 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); } 118 bool IsWakeUpPending() { return wake_up_event_.IsSignaled(); }
116 119
117 private: 120 private:
118 Thread(SchedulerWorker* outer) 121 Thread(scoped_refptr<SchedulerWorker> outer)
119 : outer_(outer), 122 : outer_(std::move(outer)),
120 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL, 123 wake_up_event_(WaitableEvent::ResetPolicy::MANUAL,
121 WaitableEvent::InitialState::NOT_SIGNALED), 124 WaitableEvent::InitialState::NOT_SIGNALED),
122 current_thread_priority_(GetDesiredThreadPriority()) { 125 current_thread_priority_(GetDesiredThreadPriority()) {
123 DCHECK(outer_); 126 DCHECK(outer_);
124 } 127 }
125 128
126 void Initialize() { 129 void Initialize() {
127 constexpr size_t kDefaultStackSize = 0; 130 constexpr size_t kDefaultStackSize = 0;
128 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_, 131 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_,
129 current_thread_priority_); 132 current_thread_priority_);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 void UpdateThreadPriority(ThreadPriority desired_thread_priority) { 171 void UpdateThreadPriority(ThreadPriority desired_thread_priority) {
169 if (desired_thread_priority == current_thread_priority_) 172 if (desired_thread_priority == current_thread_priority_)
170 return; 173 return;
171 174
172 PlatformThread::SetCurrentThreadPriority(desired_thread_priority); 175 PlatformThread::SetCurrentThreadPriority(desired_thread_priority);
173 current_thread_priority_ = desired_thread_priority; 176 current_thread_priority_ = desired_thread_priority;
174 } 177 }
175 178
176 PlatformThreadHandle thread_handle_; 179 PlatformThreadHandle thread_handle_;
177 180
178 SchedulerWorker* outer_; 181 scoped_refptr<SchedulerWorker> outer_;
179 182
180 // Event signaled to wake up this thread. 183 // Event signaled to wake up this thread.
181 WaitableEvent wake_up_event_; 184 WaitableEvent wake_up_event_;
182 185
183 // Current priority of this thread. May be different from 186 // Current priority of this thread. May be different from
184 // |outer_->priority_hint_|. 187 // |outer_->priority_hint_|.
185 ThreadPriority current_thread_priority_; 188 ThreadPriority current_thread_priority_;
186 189
187 DISALLOW_COPY_AND_ASSIGN(Thread); 190 DISALLOW_COPY_AND_ASSIGN(Thread);
188 }; 191 };
189 192
190 std::unique_ptr<SchedulerWorker> SchedulerWorker::Create( 193 scoped_refptr<SchedulerWorker> SchedulerWorker::Create(
191 ThreadPriority priority_hint, 194 ThreadPriority priority_hint,
192 std::unique_ptr<Delegate> delegate, 195 std::unique_ptr<Delegate> delegate,
193 TaskTracker* task_tracker, 196 TaskTracker* task_tracker,
194 InitialState initial_state, 197 InitialState initial_state,
195 SchedulerBackwardCompatibility backward_compatibility) { 198 SchedulerBackwardCompatibility backward_compatibility) {
196 auto worker = 199 scoped_refptr<SchedulerWorker> worker(
197 WrapUnique(new SchedulerWorker(priority_hint, std::move(delegate), 200 new SchedulerWorker(priority_hint, std::move(delegate), task_tracker,
198 task_tracker, backward_compatibility)); 201 backward_compatibility));
199 // Creation happens before any other thread can reference this one, so no 202 // Creation happens before any other thread can reference this one, so no
200 // synchronization is necessary. 203 // synchronization is necessary.
201 if (initial_state == SchedulerWorker::InitialState::ALIVE) { 204 if (initial_state == SchedulerWorker::InitialState::ALIVE) {
202 worker->CreateThread(); 205 worker->CreateThread();
203 if (!worker->thread_) { 206 if (!worker->thread_) {
204 return nullptr; 207 return nullptr;
205 } 208 }
206 } 209 }
207 210
208 return worker; 211 return worker;
209 } 212 }
210 213
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() { 214 void SchedulerWorker::WakeUp() {
219 AutoSchedulerLock auto_lock(thread_lock_); 215 AutoSchedulerLock auto_lock(thread_lock_);
220 216
221 DCHECK(!should_exit_for_testing_.IsSet()); 217 DCHECK(!join_called_for_testing_.IsSet());
222 218
223 if (!thread_) 219 if (!thread_)
224 CreateThreadAssertSynchronized(); 220 CreateThreadAssertSynchronized();
225 221
226 if (thread_) 222 if (thread_)
227 thread_->WakeUp(); 223 thread_->WakeUp();
228 } 224 }
229 225
230 void SchedulerWorker::JoinForTesting() { 226 void SchedulerWorker::JoinForTesting() {
231 DCHECK(!should_exit_for_testing_.IsSet()); 227 DCHECK(!join_called_for_testing_.IsSet());
232 should_exit_for_testing_.Set(); 228 join_called_for_testing_.Set();
233 229
234 std::unique_ptr<Thread> thread; 230 std::unique_ptr<Thread> thread;
235 231
236 { 232 {
237 AutoSchedulerLock auto_lock(thread_lock_); 233 AutoSchedulerLock auto_lock(thread_lock_);
238 234
239 if (thread_) { 235 if (thread_) {
240 // Make sure the thread is awake. It will see that 236 // Make sure the thread is awake. It will see that
241 // |should_exit_for_testing_| is set and exit shortly after. 237 // |join_called_for_testing_| is set and exit shortly after.
242 thread_->WakeUp(); 238 thread_->WakeUp();
243 thread = std::move(thread_); 239 thread = std::move(thread_);
244 } 240 }
245 } 241 }
246 242
247 if (thread) 243 if (thread)
248 thread->Join(); 244 thread->Join();
249 } 245 }
250 246
251 bool SchedulerWorker::ThreadAliveForTesting() const { 247 bool SchedulerWorker::ThreadAliveForTesting() const {
252 AutoSchedulerLock auto_lock(thread_lock_); 248 AutoSchedulerLock auto_lock(thread_lock_);
253 return !!thread_; 249 return !!thread_;
254 } 250 }
255 251
252 void SchedulerWorker::Cleanup() {
253 AutoSchedulerLock auto_lock(thread_lock_);
254 DCHECK(!should_exit_);
255 if (thread_) {
256 should_exit_ = true;
257 thread_->WakeUp();
258 }
259 }
260
256 SchedulerWorker::SchedulerWorker( 261 SchedulerWorker::SchedulerWorker(
257 ThreadPriority priority_hint, 262 ThreadPriority priority_hint,
258 std::unique_ptr<Delegate> delegate, 263 std::unique_ptr<Delegate> delegate,
259 TaskTracker* task_tracker, 264 TaskTracker* task_tracker,
260 SchedulerBackwardCompatibility backward_compatibility) 265 SchedulerBackwardCompatibility backward_compatibility)
261 : priority_hint_(priority_hint), 266 : priority_hint_(priority_hint),
262 delegate_(std::move(delegate)), 267 delegate_(std::move(delegate)),
263 task_tracker_(task_tracker) 268 task_tracker_(task_tracker)
264 #if defined(OS_WIN) 269 #if defined(OS_WIN)
265 , 270 ,
266 backward_compatibility_(backward_compatibility) 271 backward_compatibility_(backward_compatibility)
267 #endif 272 #endif
268 { 273 {
269 DCHECK(delegate_); 274 DCHECK(delegate_);
270 DCHECK(task_tracker_); 275 DCHECK(task_tracker_);
271 } 276 }
272 277
273 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::Detach() { 278 SchedulerWorker::~SchedulerWorker() {
279 // It is unexpected for |thread_| to be alive and for SchedulerWorker to
280 // destroy since SchedulerWorker owns the delegate needed by |thread_|.
281 // For testing, this generally means JoinForTesting was not called.
282 DCHECK(!thread_);
283 }
284
285 std::unique_ptr<SchedulerWorker::Thread> SchedulerWorker::DetachThreadObject(
286 DetachNotify detach_notify) {
274 AutoSchedulerLock auto_lock(thread_lock_); 287 AutoSchedulerLock auto_lock(thread_lock_);
275 288
276 // Do not detach if the thread is being joined. 289 // Do not detach if the thread is being joined.
277 if (!thread_) { 290 if (!thread_) {
278 DCHECK(should_exit_for_testing_.IsSet()); 291 DCHECK(join_called_for_testing_.IsSet());
279 return nullptr; 292 return nullptr;
280 } 293 }
281 294
282 // If a wakeup is pending, then a WakeUp() came in while we were deciding to 295 // 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 296 // detach. This means we can't go away anymore since we would break the
284 // guarantee that we call GetWork() after a successful wakeup. 297 // guarantee that we call GetWork() after a successful wakeup.
285 if (thread_->IsWakeUpPending()) 298 if (thread_->IsWakeUpPending())
286 return nullptr; 299 return nullptr;
287 300
288 // Call OnDetach() within the scope of |thread_lock_| to prevent the delegate 301 if (detach_notify == DetachNotify::DELEGATE) {
289 // from being used concurrently from an old and a new thread. 302 // Call OnDetach() within the scope of |thread_lock_| to prevent the
290 delegate_->OnDetach(); 303 // delegate from being used concurrently from an old and a new thread.
304 delegate_->OnDetach();
305 }
291 306
292 return std::move(thread_); 307 return std::move(thread_);
293 } 308 }
294 309
295 void SchedulerWorker::CreateThread() { 310 void SchedulerWorker::CreateThread() {
296 thread_ = Thread::Create(this); 311 thread_ = Thread::Create(this);
297 } 312 }
298 313
299 void SchedulerWorker::CreateThreadAssertSynchronized() { 314 void SchedulerWorker::CreateThreadAssertSynchronized() {
300 thread_lock_.AssertAcquired(); 315 thread_lock_.AssertAcquired();
301 CreateThread(); 316 CreateThread();
302 } 317 }
303 318
319 bool SchedulerWorker::ShouldExit() {
320 bool should_exit =
321 task_tracker_->IsShutdownComplete() || join_called_for_testing_.IsSet();
322 if (should_exit)
323 return true;
324
325 AutoSchedulerLock auto_lock(thread_lock_);
326 return should_exit_;
327 }
328
304 } // namespace internal 329 } // namespace internal
305 } // namespace base 330 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698