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

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

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

Powered by Google App Engine
This is Rietveld 408576698