• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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