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

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

Issue 2019763002: TaskScheduler: Atomic operations in TaskTracker (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: CR gab #10 Created 4 years, 6 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/task_tracker.h" 5 #include "base/task_scheduler/task_tracker.h"
6 6
7 #include "base/atomicops.h"
7 #include "base/callback.h" 8 #include "base/callback.h"
8 #include "base/debug/task_annotator.h" 9 #include "base/debug/task_annotator.h"
9 #include "base/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
10 #include "base/threading/sequenced_task_runner_handle.h" 11 #include "base/threading/sequenced_task_runner_handle.h"
11 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
12 #include "base/threading/thread_task_runner_handle.h" 13 #include "base/threading/thread_task_runner_handle.h"
13 #include "base/trace_event/trace_event.h" 14 #include "base/trace_event/trace_event.h"
14 15
15 namespace base { 16 namespace base {
16 namespace internal { 17 namespace internal {
(...skipping 12 matching lines...) Expand all
29 30
30 void RecordNumBlockShutdownTasksPostedDuringShutdown( 31 void RecordNumBlockShutdownTasksPostedDuringShutdown(
31 HistogramBase::Sample value) { 32 HistogramBase::Sample value) {
32 UMA_HISTOGRAM_CUSTOM_COUNTS( 33 UMA_HISTOGRAM_CUSTOM_COUNTS(
33 "TaskScheduler.BlockShutdownTasksPostedDuringShutdown", value, 1, 34 "TaskScheduler.BlockShutdownTasksPostedDuringShutdown", value, 1,
34 kMaxBlockShutdownTasksPostedDuringShutdown, 50); 35 kMaxBlockShutdownTasksPostedDuringShutdown, 50);
35 } 36 }
36 37
37 } // namespace 38 } // namespace
38 39
39 TaskTracker::TaskTracker() = default; 40 class TaskTracker::State {
41 public:
42 State() = default;
43
44 // Sets a flag indicating that shutdown has started. Returns true if there are
45 // tasks blocking shutdown. Can only be called once.
46 bool StartShutdown() {
47 const auto new_value =
48 subtle::Barrier_AtomicIncrement(&bits_, kShutdownHasStartedMask);
49
50 // Check that the "shutdown has started" bit isn't zero. This would happen
51 // if it was incremented twice.
52 DCHECK(new_value & kShutdownHasStartedMask);
53
54 const auto num_tasks_blocking_shutdown =
55 new_value / kNumTasksBlockingShutdownIncrement;
gab 2016/06/20 16:46:20 I think it would be simpler to have the shutdown b
fdoray 2016/06/20 19:31:35 I don't know how I would set the shutdown bit it i
56 return num_tasks_blocking_shutdown != 0;
57 }
58
59 // Returns true if shutdown has started.
60 bool ShutdownHasStarted() const {
61 return subtle::NoBarrier_Load(&bits_) & kShutdownHasStartedMask;
62 }
63
64 // Returns true if there are tasks blocking shutdown.
65 bool TasksAreBlockingShutdown() const {
66 const auto num_tasks_blocking_shutdown =
67 subtle::NoBarrier_Load(&bits_) / kNumTasksBlockingShutdownIncrement;
68 DCHECK_GE(num_tasks_blocking_shutdown, 0);
69 return num_tasks_blocking_shutdown != 0;
70 }
71
72 // Increments the number of tasks blocking shutdown. Returns true if shutdown
73 // has started.
74 bool IncrementNumTasksBlockingShutdown() {
75 const auto new_bits = subtle::NoBarrier_AtomicIncrement(
76 &bits_, kNumTasksBlockingShutdownIncrement);
77
78 // Verify that there is no overflow.
79 const auto num_tasks_blocking_shutdown =
80 new_bits / kNumTasksBlockingShutdownIncrement;
81 DCHECK_GE(num_tasks_blocking_shutdown, 0);
82
83 const bool shutdown_has_started = new_bits & kShutdownHasStartedMask;
84 return shutdown_has_started;
85 }
86
87 // Decrements the number of tasks blocking shutdown. Returns true if shutdown
88 // has started and the number of tasks blocking shutdown becomes zero.
89 bool DecrementNumTasksBlockingShutdown() {
90 const auto new_bits = subtle::NoBarrier_AtomicIncrement(
91 &bits_, -kNumTasksBlockingShutdownIncrement);
92 const bool shutdown_has_started = new_bits & kShutdownHasStartedMask;
93 const auto num_tasks_blocking_shutdown =
94 new_bits / kNumTasksBlockingShutdownIncrement;
95 DCHECK_GE(num_tasks_blocking_shutdown, 0);
96 return shutdown_has_started && num_tasks_blocking_shutdown == 0;
97 }
98
99 private:
100 static constexpr subtle::Atomic32 kShutdownHasStartedMask = 1;
101 static constexpr subtle::Atomic32 kNumTasksBlockingShutdownIncrement = 2;
102
103 // The LSB indicates whether shutdown has started. The other bits count the
104 // number of tasks blocking shutdown.
105 subtle::Atomic32 bits_ = 0;
106
107 DISALLOW_COPY_AND_ASSIGN(State);
108 };
109
110 TaskTracker::TaskTracker() : state_(new State) {}
40 TaskTracker::~TaskTracker() = default; 111 TaskTracker::~TaskTracker() = default;
41 112
42 void TaskTracker::Shutdown() { 113 void TaskTracker::Shutdown() {
43 AutoSchedulerLock auto_lock(lock_); 114 {
115 AutoSchedulerLock auto_lock(lock_);
44 116
45 // This method should only be called once. 117 // This method can only be called once.
46 DCHECK(!shutdown_completed_); 118 DCHECK(!shutdown_event_);
47 DCHECK(!shutdown_cv_); 119 DCHECK(!num_block_shutdown_tasks_posted_during_shutdown_);
48 120
49 shutdown_cv_ = lock_.CreateConditionVariable(); 121 shutdown_event_.reset(
122 new WaitableEvent(WaitableEvent::ResetPolicy::MANUAL,
123 WaitableEvent::InitialState::NOT_SIGNALED));
50 124
51 // Wait until the number of tasks blocking shutdown is zero. 125 const bool tasks_are_blocking_shutdown = state_->StartShutdown();
52 while (num_tasks_blocking_shutdown_ != 0)
53 shutdown_cv_->Wait();
54 126
55 shutdown_cv_.reset(); 127 // From now, if a thread causes the number of tasks blocking shutdown to
56 shutdown_completed_ = true; 128 // become zero, it will call OnBlockingShutdownTasksComplete().
57 129
58 // Record the TaskScheduler.BlockShutdownTasksPostedDuringShutdown if less 130 if (!tasks_are_blocking_shutdown) {
59 // than |kMaxBlockShutdownTasksPostedDuringShutdown| BLOCK_SHUTDOWN tasks were 131 shutdown_event_->Signal();
60 // posted during shutdown. Otherwise, the histogram has already been recorded 132 return;
61 // in BeforePostTask(). 133 }
62 if (num_block_shutdown_tasks_posted_during_shutdown_ < 134 }
63 kMaxBlockShutdownTasksPostedDuringShutdown) { 135
64 RecordNumBlockShutdownTasksPostedDuringShutdown( 136 // It is safe to access |shutdown_event_| without holding |lock_| because the
65 num_block_shutdown_tasks_posted_during_shutdown_); 137 // pointer never changes after being set above.
138 shutdown_event_->Wait();
139
140 {
141 AutoSchedulerLock auto_lock(lock_);
142
143 // Record TaskScheduler.BlockShutdownTasksPostedDuringShutdown if less than
144 // |kMaxBlockShutdownTasksPostedDuringShutdown| BLOCK_SHUTDOWN tasks were
145 // posted during shutdown. Otherwise, the histogram has already been
146 // recorded in BeforePostTask().
147 if (num_block_shutdown_tasks_posted_during_shutdown_ <
148 kMaxBlockShutdownTasksPostedDuringShutdown) {
149 RecordNumBlockShutdownTasksPostedDuringShutdown(
150 num_block_shutdown_tasks_posted_during_shutdown_);
151 }
66 } 152 }
67 } 153 }
68 154
69 bool TaskTracker::WillPostTask(const Task* task) { 155 bool TaskTracker::WillPostTask(const Task* task) {
70 DCHECK(task); 156 DCHECK(task);
71 157
72 if (!BeforePostTask(task->traits.shutdown_behavior())) 158 if (!BeforePostTask(task->traits.shutdown_behavior()))
73 return false; 159 return false;
74 160
75 debug::TaskAnnotator task_annotator; 161 debug::TaskAnnotator task_annotator;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 195
110 TRACE_TASK_EXECUTION(kRunFunctionName, *task); 196 TRACE_TASK_EXECUTION(kRunFunctionName, *task);
111 197
112 debug::TaskAnnotator task_annotator; 198 debug::TaskAnnotator task_annotator;
113 task_annotator.RunTask(kQueueFunctionName, *task); 199 task_annotator.RunTask(kQueueFunctionName, *task);
114 } 200 }
115 201
116 AfterRunTask(shutdown_behavior); 202 AfterRunTask(shutdown_behavior);
117 } 203 }
118 204
205 bool TaskTracker::ShutdownCompleted() const {
206 AutoSchedulerLock auto_lock(lock_);
207 return shutdown_event_ && shutdown_event_->IsSignaled();
208 }
209
119 bool TaskTracker::IsShuttingDownForTesting() const { 210 bool TaskTracker::IsShuttingDownForTesting() const {
120 AutoSchedulerLock auto_lock(lock_); 211 AutoSchedulerLock auto_lock(lock_);
121 return !!shutdown_cv_; 212 return !!shutdown_event_ && !shutdown_event_->IsSignaled();
gab 2016/06/20 16:46:20 Don't think you need !! because the && explicitly
fdoray 2016/06/20 19:31:35 Done.
122 } 213 }
123 214
124 bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) { 215 bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
125 AutoSchedulerLock auto_lock(lock_);
126
127 if (shutdown_completed_) {
128 // A BLOCK_SHUTDOWN task posted after shutdown has completed is an ordering
129 // bug. This DCHECK aims to catch those early.
130 DCHECK_NE(shutdown_behavior, TaskShutdownBehavior::BLOCK_SHUTDOWN);
131
132 // No task is allowed to be posted after shutdown.
133 return false;
134 }
135
136 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) { 216 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
137 // BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted 217 // BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted
138 // and the moment they complete their execution. 218 // and the moment they complete their execution.
139 ++num_tasks_blocking_shutdown_; 219 const bool shutdown_started = state_->IncrementNumTasksBlockingShutdown();
140 220
141 if (shutdown_cv_) { 221 if (shutdown_started) {
222 AutoSchedulerLock auto_lock(lock_);
223
224 // A BLOCK_SHUTDOWN task posted after shutdown has completed is an
225 // ordering bug. This aims to catch those early.
226 DCHECK(shutdown_event_);
227 DCHECK(!shutdown_event_->IsSignaled());
228
142 ++num_block_shutdown_tasks_posted_during_shutdown_; 229 ++num_block_shutdown_tasks_posted_during_shutdown_;
143 230
144 if (num_block_shutdown_tasks_posted_during_shutdown_ == 231 if (num_block_shutdown_tasks_posted_during_shutdown_ ==
145 kMaxBlockShutdownTasksPostedDuringShutdown) { 232 kMaxBlockShutdownTasksPostedDuringShutdown) {
146 // Record the TaskScheduler.BlockShutdownTasksPostedDuringShutdown 233 // Record the TaskScheduler.BlockShutdownTasksPostedDuringShutdown
147 // histogram as soon as its upper bound is hit. That way, a value will 234 // histogram as soon as its upper bound is hit. That way, a value will
148 // be recorded even if an infinite number of BLOCK_SHUTDOWN tasks are 235 // be recorded even if an infinite number of BLOCK_SHUTDOWN tasks are
149 // posted, preventing shutdown to complete. 236 // posted, preventing shutdown to complete.
150 RecordNumBlockShutdownTasksPostedDuringShutdown( 237 RecordNumBlockShutdownTasksPostedDuringShutdown(
151 num_block_shutdown_tasks_posted_during_shutdown_); 238 num_block_shutdown_tasks_posted_during_shutdown_);
152 } 239 }
153 } 240 }
154 241
155 // A BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't
156 // completed.
157 return true; 242 return true;
158 } 243 }
159 244
160 // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't 245 // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't
161 // started. 246 // started.
162 return !shutdown_cv_; 247 return !state_->ShutdownHasStarted();
163 } 248 }
164 249
165 bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) { 250 bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) {
166 AutoSchedulerLock auto_lock(lock_); 251 switch (shutdown_behavior) {
252 case TaskShutdownBehavior::BLOCK_SHUTDOWN: {
253 // The number of tasks blocking shutdown has been incremented when the
254 // task was posted.
255 DCHECK(state_->TasksAreBlockingShutdown());
167 256
168 if (shutdown_completed_) { 257 // Trying to run a BLOCK_SHUTDOWN task after shutdown has completed is
169 // Trying to run a BLOCK_SHUTDOWN task after shutdown has completed is 258 // unexpected as it either shouldn't have been posted if shutdown
170 // unexpected as it either shouldn't have been posted if shutdown completed 259 // completed or should be blocking shutdown if it was posted before it
171 // or should be blocking shutdown if it was posted before it did. 260 // did.
172 DCHECK_NE(shutdown_behavior, TaskShutdownBehavior::BLOCK_SHUTDOWN); 261 DCHECK(!state_->ShutdownHasStarted() || !ShutdownCompleted());
173 262
174 // A WorkerThread might extract a non BLOCK_SHUTDOWN task from a 263 return true;
175 // PriorityQueue after shutdown. It shouldn't be allowed to run it. 264 }
176 return false;
177 }
178 265
179 switch (shutdown_behavior) { 266 case TaskShutdownBehavior::SKIP_ON_SHUTDOWN: {
180 case TaskShutdownBehavior::BLOCK_SHUTDOWN: 267 // SKIP_ON_SHUTDOWN tasks block shutdown while they are running.
181 DCHECK_GT(num_tasks_blocking_shutdown_, 0U); 268 const bool shutdown_started = state_->IncrementNumTasksBlockingShutdown();
269
270 if (shutdown_started) {
271 // The SKIP_ON_SHUTDOWN task isn't allowed to run during shutdown.
272 // Decrement the number of tasks blocking shutdown that was wrongly
273 // incremented.
274 //
275 // Note: Reading the "shutdown initiated" bit and then incrementing the
276 // number of tasks blocking shutdown if it isn't set (without re-
277 // checking it afterwards) wouldn't work because the bit can be set
278 // between the two operations.
gab 2016/06/20 16:46:20 As mentioned in my CL description comments I don't
fdoray 2016/06/20 19:31:35 Done.
279 const bool shutdown_started_and_no_tasks_block_shutdown =
280 state_->DecrementNumTasksBlockingShutdown();
281 if (shutdown_started_and_no_tasks_block_shutdown)
282 OnBlockingShutdownTasksComplete();
283
284 return false;
285 }
286
182 return true; 287 return true;
288 }
183 289
184 case TaskShutdownBehavior::SKIP_ON_SHUTDOWN: 290 case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN: {
185 if (shutdown_cv_) 291 return !state_->ShutdownHasStarted();
186 return false; 292 }
187
188 // SKIP_ON_SHUTDOWN tasks block shutdown while they are running.
189 ++num_tasks_blocking_shutdown_;
190 return true;
191
192 case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN:
193 return !shutdown_cv_;
194 } 293 }
195 294
196 NOTREACHED(); 295 NOTREACHED();
197 return false; 296 return false;
198 } 297 }
199 298
200 void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) { 299 void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) {
201 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN || 300 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN ||
202 shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) { 301 shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {
203 AutoSchedulerLock auto_lock(lock_); 302 const bool shutdown_started_and_no_tasks_block_shutdown =
204 DCHECK_GT(num_tasks_blocking_shutdown_, 0U); 303 state_->DecrementNumTasksBlockingShutdown();
205 --num_tasks_blocking_shutdown_; 304 if (shutdown_started_and_no_tasks_block_shutdown)
206 if (num_tasks_blocking_shutdown_ == 0 && shutdown_cv_) 305 OnBlockingShutdownTasksComplete();
207 shutdown_cv_->Signal();
208 } 306 }
209 } 307 }
210 308
309 void TaskTracker::OnBlockingShutdownTasksComplete() {
310 AutoSchedulerLock auto_lock(lock_);
311
312 // This method can only be called after shutdown has started.
313 DCHECK(state_->ShutdownHasStarted());
314 DCHECK(shutdown_event_);
315
316 shutdown_event_->Signal();
317 }
318
211 } // namespace internal 319 } // namespace internal
212 } // namespace base 320 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698