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
3 // found in the LICENSE file.
4
5 #include "base/task_scheduler/scheduler_worker.h"
6
7 #include <stddef.h>
8
9 #include <utility>
10
11 #include "base/compiler_specific.h"
12 #include "base/debug/alias.h"
13 #include "base/logging.h"
14 #include "base/task_scheduler/environment_config.h"
15 #include "base/task_scheduler/scheduler_worker_observer.h"
16 #include "base/task_scheduler/task_tracker.h"
17 #include "base/trace_event/trace_event.h"
18
19 #if defined(OS_MACOSX)
20 #include "base/mac/scoped_nsautorelease_pool.h"
21 #elif defined(OS_WIN)
22 #include "base/win/com_init_check_hook.h"
23 #include "base/win/scoped_com_initializer.h"
24 #endif
25
26 namespace base {
27 namespace internal {
28
WaitForWork(WaitableEvent * wake_up_event)29 void SchedulerWorker::Delegate::WaitForWork(WaitableEvent* wake_up_event) {
30 DCHECK(wake_up_event);
31 const TimeDelta sleep_time = GetSleepTimeout();
32 if (sleep_time.is_max()) {
33 // Calling TimedWait with TimeDelta::Max is not recommended per
34 // http://crbug.com/465948.
35 wake_up_event->Wait();
36 } else {
37 wake_up_event->TimedWait(sleep_time);
38 }
39 }
40
SchedulerWorker(ThreadPriority priority_hint,std::unique_ptr<Delegate> delegate,TrackedRef<TaskTracker> task_tracker,const SchedulerLock * predecessor_lock,SchedulerBackwardCompatibility backward_compatibility)41 SchedulerWorker::SchedulerWorker(
42 ThreadPriority priority_hint,
43 std::unique_ptr<Delegate> delegate,
44 TrackedRef<TaskTracker> task_tracker,
45 const SchedulerLock* predecessor_lock,
46 SchedulerBackwardCompatibility backward_compatibility)
47 : thread_lock_(predecessor_lock),
48 delegate_(std::move(delegate)),
49 task_tracker_(std::move(task_tracker)),
50 priority_hint_(priority_hint),
51 current_thread_priority_(GetDesiredThreadPriority())
52 #if defined(OS_WIN) && !defined(COM_INIT_CHECK_HOOK_ENABLED)
53 ,
54 backward_compatibility_(backward_compatibility)
55 #endif
56 {
57 DCHECK(delegate_);
58 DCHECK(task_tracker_);
59 DCHECK(CanUseBackgroundPriorityForSchedulerWorker() ||
60 priority_hint_ != ThreadPriority::BACKGROUND);
61 }
62
Start(SchedulerWorkerObserver * scheduler_worker_observer)63 bool SchedulerWorker::Start(
64 SchedulerWorkerObserver* scheduler_worker_observer) {
65 AutoSchedulerLock auto_lock(thread_lock_);
66 DCHECK(thread_handle_.is_null());
67
68 if (should_exit_.IsSet())
69 return true;
70
71 DCHECK(!scheduler_worker_observer_);
72 scheduler_worker_observer_ = scheduler_worker_observer;
73
74 self_ = this;
75
76 constexpr size_t kDefaultStackSize = 0;
77 PlatformThread::CreateWithPriority(kDefaultStackSize, this, &thread_handle_,
78 current_thread_priority_);
79
80 if (thread_handle_.is_null()) {
81 self_ = nullptr;
82 return false;
83 }
84
85 return true;
86 }
87
WakeUp()88 void SchedulerWorker::WakeUp() {
89 // Calling WakeUp() after Cleanup() or Join() is wrong because the
90 // SchedulerWorker cannot run more tasks.
91 DCHECK(!join_called_for_testing_.IsSet());
92 DCHECK(!should_exit_.IsSet());
93 wake_up_event_.Signal();
94 }
95
JoinForTesting()96 void SchedulerWorker::JoinForTesting() {
97 DCHECK(!join_called_for_testing_.IsSet());
98 join_called_for_testing_.Set();
99 wake_up_event_.Signal();
100
101 PlatformThreadHandle thread_handle;
102
103 {
104 AutoSchedulerLock auto_lock(thread_lock_);
105 DCHECK(!thread_handle_.is_null());
106 thread_handle = thread_handle_;
107 // Reset |thread_handle_| so it isn't joined by the destructor.
108 thread_handle_ = PlatformThreadHandle();
109 }
110
111 PlatformThread::Join(thread_handle);
112 }
113
ThreadAliveForTesting() const114 bool SchedulerWorker::ThreadAliveForTesting() const {
115 AutoSchedulerLock auto_lock(thread_lock_);
116 return !thread_handle_.is_null();
117 }
118
~SchedulerWorker()119 SchedulerWorker::~SchedulerWorker() {
120 AutoSchedulerLock auto_lock(thread_lock_);
121
122 // If |thread_handle_| wasn't joined, detach it.
123 if (!thread_handle_.is_null()) {
124 DCHECK(!join_called_for_testing_.IsSet());
125 PlatformThread::Detach(thread_handle_);
126 }
127 }
128
Cleanup()129 void SchedulerWorker::Cleanup() {
130 DCHECK(!should_exit_.IsSet());
131 should_exit_.Set();
132 wake_up_event_.Signal();
133 }
134
BeginUnusedPeriod()135 void SchedulerWorker::BeginUnusedPeriod() {
136 AutoSchedulerLock auto_lock(thread_lock_);
137 DCHECK(last_used_time_.is_null());
138 last_used_time_ = TimeTicks::Now();
139 }
140
EndUnusedPeriod()141 void SchedulerWorker::EndUnusedPeriod() {
142 AutoSchedulerLock auto_lock(thread_lock_);
143 DCHECK(!last_used_time_.is_null());
144 last_used_time_ = TimeTicks();
145 }
146
GetLastUsedTime() const147 TimeTicks SchedulerWorker::GetLastUsedTime() const {
148 AutoSchedulerLock auto_lock(thread_lock_);
149 return last_used_time_;
150 }
151
ShouldExit() const152 bool SchedulerWorker::ShouldExit() const {
153 // The ordering of the checks is important below. This SchedulerWorker may be
154 // released and outlive |task_tracker_| in unit tests. However, when the
155 // SchedulerWorker is released, |should_exit_| will be set, so check that
156 // first.
157 return should_exit_.IsSet() || join_called_for_testing_.IsSet() ||
158 task_tracker_->IsShutdownComplete();
159 }
160
GetDesiredThreadPriority() const161 ThreadPriority SchedulerWorker::GetDesiredThreadPriority() const {
162 // To avoid shutdown hangs, disallow a priority below NORMAL during shutdown
163 if (task_tracker_->HasShutdownStarted())
164 return ThreadPriority::NORMAL;
165
166 return priority_hint_;
167 }
168
UpdateThreadPriority(ThreadPriority desired_thread_priority)169 void SchedulerWorker::UpdateThreadPriority(
170 ThreadPriority desired_thread_priority) {
171 if (desired_thread_priority == current_thread_priority_)
172 return;
173
174 PlatformThread::SetCurrentThreadPriority(desired_thread_priority);
175 current_thread_priority_ = desired_thread_priority;
176 }
177
ThreadMain()178 void SchedulerWorker::ThreadMain() {
179 if (priority_hint_ == ThreadPriority::BACKGROUND) {
180 switch (delegate_->GetThreadLabel()) {
181 case ThreadLabel::POOLED:
182 RunBackgroundPooledWorker();
183 return;
184 case ThreadLabel::SHARED:
185 RunBackgroundSharedWorker();
186 return;
187 case ThreadLabel::DEDICATED:
188 RunBackgroundDedicatedWorker();
189 return;
190 #if defined(OS_WIN)
191 case ThreadLabel::SHARED_COM:
192 RunBackgroundSharedCOMWorker();
193 return;
194 case ThreadLabel::DEDICATED_COM:
195 RunBackgroundDedicatedCOMWorker();
196 return;
197 #endif // defined(OS_WIN)
198 }
199 }
200
201 switch (delegate_->GetThreadLabel()) {
202 case ThreadLabel::POOLED:
203 RunPooledWorker();
204 return;
205 case ThreadLabel::SHARED:
206 RunSharedWorker();
207 return;
208 case ThreadLabel::DEDICATED:
209 RunDedicatedWorker();
210 return;
211 #if defined(OS_WIN)
212 case ThreadLabel::SHARED_COM:
213 RunSharedCOMWorker();
214 return;
215 case ThreadLabel::DEDICATED_COM:
216 RunDedicatedCOMWorker();
217 return;
218 #endif // defined(OS_WIN)
219 }
220 }
221
RunPooledWorker()222 NOINLINE void SchedulerWorker::RunPooledWorker() {
223 const int line_number = __LINE__;
224 RunWorker();
225 base::debug::Alias(&line_number);
226 }
227
RunBackgroundPooledWorker()228 NOINLINE void SchedulerWorker::RunBackgroundPooledWorker() {
229 const int line_number = __LINE__;
230 RunWorker();
231 base::debug::Alias(&line_number);
232 }
233
RunSharedWorker()234 NOINLINE void SchedulerWorker::RunSharedWorker() {
235 const int line_number = __LINE__;
236 RunWorker();
237 base::debug::Alias(&line_number);
238 }
239
RunBackgroundSharedWorker()240 NOINLINE void SchedulerWorker::RunBackgroundSharedWorker() {
241 const int line_number = __LINE__;
242 RunWorker();
243 base::debug::Alias(&line_number);
244 }
245
RunDedicatedWorker()246 NOINLINE void SchedulerWorker::RunDedicatedWorker() {
247 const int line_number = __LINE__;
248 RunWorker();
249 base::debug::Alias(&line_number);
250 }
251
RunBackgroundDedicatedWorker()252 NOINLINE void SchedulerWorker::RunBackgroundDedicatedWorker() {
253 const int line_number = __LINE__;
254 RunWorker();
255 base::debug::Alias(&line_number);
256 }
257
258 #if defined(OS_WIN)
RunSharedCOMWorker()259 NOINLINE void SchedulerWorker::RunSharedCOMWorker() {
260 const int line_number = __LINE__;
261 RunWorker();
262 base::debug::Alias(&line_number);
263 }
264
RunBackgroundSharedCOMWorker()265 NOINLINE void SchedulerWorker::RunBackgroundSharedCOMWorker() {
266 const int line_number = __LINE__;
267 RunWorker();
268 base::debug::Alias(&line_number);
269 }
270
RunDedicatedCOMWorker()271 NOINLINE void SchedulerWorker::RunDedicatedCOMWorker() {
272 const int line_number = __LINE__;
273 RunWorker();
274 base::debug::Alias(&line_number);
275 }
276
RunBackgroundDedicatedCOMWorker()277 NOINLINE void SchedulerWorker::RunBackgroundDedicatedCOMWorker() {
278 const int line_number = __LINE__;
279 RunWorker();
280 base::debug::Alias(&line_number);
281 }
282 #endif // defined(OS_WIN)
283
RunWorker()284 void SchedulerWorker::RunWorker() {
285 DCHECK_EQ(self_, this);
286 TRACE_EVENT_BEGIN0("task_scheduler", "SchedulerWorkerThread active");
287
288 if (scheduler_worker_observer_)
289 scheduler_worker_observer_->OnSchedulerWorkerMainEntry();
290
291 delegate_->OnMainEntry(this);
292
293 // A SchedulerWorker starts out waiting for work.
294 {
295 TRACE_EVENT_END0("task_scheduler", "SchedulerWorkerThread active");
296 delegate_->WaitForWork(&wake_up_event_);
297 TRACE_EVENT_BEGIN0("task_scheduler", "SchedulerWorkerThread active");
298 }
299
300 // When defined(COM_INIT_CHECK_HOOK_ENABLED), ignore
301 // SchedulerBackwardCompatibility::INIT_COM_STA to find incorrect uses of
302 // COM that should be running in a COM STA Task Runner.
303 #if defined(OS_WIN) && !defined(COM_INIT_CHECK_HOOK_ENABLED)
304 std::unique_ptr<win::ScopedCOMInitializer> com_initializer;
305 if (backward_compatibility_ == SchedulerBackwardCompatibility::INIT_COM_STA)
306 com_initializer = std::make_unique<win::ScopedCOMInitializer>();
307 #endif
308
309 while (!ShouldExit()) {
310 #if defined(OS_MACOSX)
311 mac::ScopedNSAutoreleasePool autorelease_pool;
312 #endif
313
314 UpdateThreadPriority(GetDesiredThreadPriority());
315
316 // Get the sequence containing the next task to execute.
317 scoped_refptr<Sequence> sequence = delegate_->GetWork(this);
318 if (!sequence) {
319 // Exit immediately if GetWork() resulted in detaching this worker.
320 if (ShouldExit())
321 break;
322
323 TRACE_EVENT_END0("task_scheduler", "SchedulerWorkerThread active");
324 delegate_->WaitForWork(&wake_up_event_);
325 TRACE_EVENT_BEGIN0("task_scheduler", "SchedulerWorkerThread active");
326 continue;
327 }
328
329 sequence =
330 task_tracker_->RunAndPopNextTask(std::move(sequence), delegate_.get());
331
332 delegate_->DidRunTask();
333
334 // Re-enqueue |sequence| if allowed by RunNextTask().
335 if (sequence)
336 delegate_->ReEnqueueSequence(std::move(sequence));
337
338 // Calling WakeUp() guarantees that this SchedulerWorker will run Tasks from
339 // Sequences returned by the GetWork() method of |delegate_| until it
340 // returns nullptr. Resetting |wake_up_event_| here doesn't break this
341 // invariant and avoids a useless loop iteration before going to sleep if
342 // WakeUp() is called while this SchedulerWorker is awake.
343 wake_up_event_.Reset();
344 }
345
346 // Important: It is unsafe to access unowned state (e.g. |task_tracker_|)
347 // after invoking OnMainExit().
348
349 delegate_->OnMainExit(this);
350
351 if (scheduler_worker_observer_)
352 scheduler_worker_observer_->OnSchedulerWorkerMainExit();
353
354 // Release the self-reference to |this|. This can result in deleting |this|
355 // and as such no more member accesses should be made after this point.
356 self_ = nullptr;
357
358 TRACE_EVENT_END0("task_scheduler", "SchedulerWorkerThread active");
359 }
360
361 } // namespace internal
362 } // namespace base
363