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

Unified 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, 7 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 side-by-side diff with in-line comments
Download patch
Index: base/task_scheduler/task_tracker.cc
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index 9e85b5cfc17b7b58c890e964294fbfc0f3680e72..d964d1c5f715036cfa8d42697cd5dd7dc3e5c0cf 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -4,6 +4,7 @@
#include "base/task_scheduler/task_tracker.h"
+#include "base/atomicops.h"
#include "base/callback.h"
#include "base/debug/task_annotator.h"
#include "base/metrics/histogram_macros.h"
@@ -36,33 +37,118 @@ void RecordNumBlockShutdownTasksPostedDuringShutdown(
} // namespace
-TaskTracker::TaskTracker() = default;
+class TaskTracker::State {
+ public:
+ State() = default;
+
+ // Sets a flag indicating that shutdown has started. Returns true if there are
+ // tasks blocking shutdown. Can only be called once.
+ bool StartShutdown() {
+ const auto new_value =
+ subtle::Barrier_AtomicIncrement(&bits_, kShutdownHasStartedMask);
+
+ // Check that the "shutdown has started" bit isn't zero. This would happen
+ // if it was incremented twice.
+ DCHECK(new_value & kShutdownHasStartedMask);
+
+ const auto num_tasks_blocking_shutdown =
+ 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
+ return num_tasks_blocking_shutdown != 0;
+ }
+
+ // Returns true if shutdown has started.
+ bool ShutdownHasStarted() const {
+ return subtle::NoBarrier_Load(&bits_) & kShutdownHasStartedMask;
+ }
+
+ // Returns true if there are tasks blocking shutdown.
+ bool TasksAreBlockingShutdown() const {
+ const auto num_tasks_blocking_shutdown =
+ subtle::NoBarrier_Load(&bits_) / kNumTasksBlockingShutdownIncrement;
+ DCHECK_GE(num_tasks_blocking_shutdown, 0);
+ return num_tasks_blocking_shutdown != 0;
+ }
+
+ // Increments the number of tasks blocking shutdown. Returns true if shutdown
+ // has started.
+ bool IncrementNumTasksBlockingShutdown() {
+ const auto new_bits = subtle::NoBarrier_AtomicIncrement(
+ &bits_, kNumTasksBlockingShutdownIncrement);
+
+ // Verify that there is no overflow.
+ const auto num_tasks_blocking_shutdown =
+ new_bits / kNumTasksBlockingShutdownIncrement;
+ DCHECK_GE(num_tasks_blocking_shutdown, 0);
+
+ const bool shutdown_has_started = new_bits & kShutdownHasStartedMask;
+ return shutdown_has_started;
+ }
+
+ // Decrements the number of tasks blocking shutdown. Returns true if shutdown
+ // has started and the number of tasks blocking shutdown becomes zero.
+ bool DecrementNumTasksBlockingShutdown() {
+ const auto new_bits = subtle::NoBarrier_AtomicIncrement(
+ &bits_, -kNumTasksBlockingShutdownIncrement);
+ const bool shutdown_has_started = new_bits & kShutdownHasStartedMask;
+ const auto num_tasks_blocking_shutdown =
+ new_bits / kNumTasksBlockingShutdownIncrement;
+ DCHECK_GE(num_tasks_blocking_shutdown, 0);
+ return shutdown_has_started && num_tasks_blocking_shutdown == 0;
+ }
+
+ private:
+ static constexpr subtle::Atomic32 kShutdownHasStartedMask = 1;
+ static constexpr subtle::Atomic32 kNumTasksBlockingShutdownIncrement = 2;
+
+ // The LSB indicates whether shutdown has started. The other bits count the
+ // number of tasks blocking shutdown.
+ subtle::Atomic32 bits_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(State);
+};
+
+TaskTracker::TaskTracker() : state_(new State) {}
TaskTracker::~TaskTracker() = default;
void TaskTracker::Shutdown() {
- AutoSchedulerLock auto_lock(lock_);
+ {
+ AutoSchedulerLock auto_lock(lock_);
- // This method should only be called once.
- DCHECK(!shutdown_completed_);
- DCHECK(!shutdown_cv_);
+ // This method can only be called once.
+ DCHECK(!shutdown_event_);
+ DCHECK(!num_block_shutdown_tasks_posted_during_shutdown_);
- shutdown_cv_ = lock_.CreateConditionVariable();
+ shutdown_event_.reset(
+ new WaitableEvent(WaitableEvent::ResetPolicy::MANUAL,
+ WaitableEvent::InitialState::NOT_SIGNALED));
- // Wait until the number of tasks blocking shutdown is zero.
- while (num_tasks_blocking_shutdown_ != 0)
- shutdown_cv_->Wait();
+ const bool tasks_are_blocking_shutdown = state_->StartShutdown();
- shutdown_cv_.reset();
- shutdown_completed_ = true;
+ // From now, if a thread causes the number of tasks blocking shutdown to
+ // become zero, it will call OnBlockingShutdownTasksComplete().
- // Record the TaskScheduler.BlockShutdownTasksPostedDuringShutdown if less
- // than |kMaxBlockShutdownTasksPostedDuringShutdown| BLOCK_SHUTDOWN tasks were
- // posted during shutdown. Otherwise, the histogram has already been recorded
- // in BeforePostTask().
- if (num_block_shutdown_tasks_posted_during_shutdown_ <
- kMaxBlockShutdownTasksPostedDuringShutdown) {
- RecordNumBlockShutdownTasksPostedDuringShutdown(
- num_block_shutdown_tasks_posted_during_shutdown_);
+ if (!tasks_are_blocking_shutdown) {
+ shutdown_event_->Signal();
+ return;
+ }
+ }
+
+ // It is safe to access |shutdown_event_| without holding |lock_| because the
+ // pointer never changes after being set above.
+ shutdown_event_->Wait();
+
+ {
+ AutoSchedulerLock auto_lock(lock_);
+
+ // Record TaskScheduler.BlockShutdownTasksPostedDuringShutdown if less than
+ // |kMaxBlockShutdownTasksPostedDuringShutdown| BLOCK_SHUTDOWN tasks were
+ // posted during shutdown. Otherwise, the histogram has already been
+ // recorded in BeforePostTask().
+ if (num_block_shutdown_tasks_posted_during_shutdown_ <
+ kMaxBlockShutdownTasksPostedDuringShutdown) {
+ RecordNumBlockShutdownTasksPostedDuringShutdown(
+ num_block_shutdown_tasks_posted_during_shutdown_);
+ }
}
}
@@ -116,29 +202,30 @@ void TaskTracker::RunTask(const Task* task) {
AfterRunTask(shutdown_behavior);
}
-bool TaskTracker::IsShuttingDownForTesting() const {
+bool TaskTracker::ShutdownCompleted() const {
AutoSchedulerLock auto_lock(lock_);
- return !!shutdown_cv_;
+ return shutdown_event_ && shutdown_event_->IsSignaled();
}
-bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
+bool TaskTracker::IsShuttingDownForTesting() const {
AutoSchedulerLock auto_lock(lock_);
+ 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.
+}
- if (shutdown_completed_) {
- // A BLOCK_SHUTDOWN task posted after shutdown has completed is an ordering
- // bug. This DCHECK aims to catch those early.
- DCHECK_NE(shutdown_behavior, TaskShutdownBehavior::BLOCK_SHUTDOWN);
-
- // No task is allowed to be posted after shutdown.
- return false;
- }
-
+bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
// BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted
// and the moment they complete their execution.
- ++num_tasks_blocking_shutdown_;
+ const bool shutdown_started = state_->IncrementNumTasksBlockingShutdown();
+
+ if (shutdown_started) {
+ AutoSchedulerLock auto_lock(lock_);
+
+ // A BLOCK_SHUTDOWN task posted after shutdown has completed is an
+ // ordering bug. This aims to catch those early.
+ DCHECK(shutdown_event_);
+ DCHECK(!shutdown_event_->IsSignaled());
- if (shutdown_cv_) {
++num_block_shutdown_tasks_posted_during_shutdown_;
if (num_block_shutdown_tasks_posted_during_shutdown_ ==
@@ -152,45 +239,57 @@ bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
}
}
- // A BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't
- // completed.
return true;
}
// A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't
// started.
- return !shutdown_cv_;
+ return !state_->ShutdownHasStarted();
}
bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) {
- AutoSchedulerLock auto_lock(lock_);
-
- if (shutdown_completed_) {
- // Trying to run a BLOCK_SHUTDOWN task after shutdown has completed is
- // unexpected as it either shouldn't have been posted if shutdown completed
- // or should be blocking shutdown if it was posted before it did.
- DCHECK_NE(shutdown_behavior, TaskShutdownBehavior::BLOCK_SHUTDOWN);
+ switch (shutdown_behavior) {
+ case TaskShutdownBehavior::BLOCK_SHUTDOWN: {
+ // The number of tasks blocking shutdown has been incremented when the
+ // task was posted.
+ DCHECK(state_->TasksAreBlockingShutdown());
- // A WorkerThread might extract a non BLOCK_SHUTDOWN task from a
- // PriorityQueue after shutdown. It shouldn't be allowed to run it.
- return false;
- }
+ // Trying to run a BLOCK_SHUTDOWN task after shutdown has completed is
+ // unexpected as it either shouldn't have been posted if shutdown
+ // completed or should be blocking shutdown if it was posted before it
+ // did.
+ DCHECK(!state_->ShutdownHasStarted() || !ShutdownCompleted());
- switch (shutdown_behavior) {
- case TaskShutdownBehavior::BLOCK_SHUTDOWN:
- DCHECK_GT(num_tasks_blocking_shutdown_, 0U);
return true;
+ }
+
+ case TaskShutdownBehavior::SKIP_ON_SHUTDOWN: {
+ // SKIP_ON_SHUTDOWN tasks block shutdown while they are running.
+ const bool shutdown_started = state_->IncrementNumTasksBlockingShutdown();
+
+ if (shutdown_started) {
+ // The SKIP_ON_SHUTDOWN task isn't allowed to run during shutdown.
+ // Decrement the number of tasks blocking shutdown that was wrongly
+ // incremented.
+ //
+ // Note: Reading the "shutdown initiated" bit and then incrementing the
+ // number of tasks blocking shutdown if it isn't set (without re-
+ // checking it afterwards) wouldn't work because the bit can be set
+ // 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.
+ const bool shutdown_started_and_no_tasks_block_shutdown =
+ state_->DecrementNumTasksBlockingShutdown();
+ if (shutdown_started_and_no_tasks_block_shutdown)
+ OnBlockingShutdownTasksComplete();
- case TaskShutdownBehavior::SKIP_ON_SHUTDOWN:
- if (shutdown_cv_)
return false;
+ }
- // SKIP_ON_SHUTDOWN tasks block shutdown while they are running.
- ++num_tasks_blocking_shutdown_;
return true;
+ }
- case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN:
- return !shutdown_cv_;
+ case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN: {
+ return !state_->ShutdownHasStarted();
+ }
}
NOTREACHED();
@@ -200,13 +299,22 @@ bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) {
void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) {
if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN ||
shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) {
- AutoSchedulerLock auto_lock(lock_);
- DCHECK_GT(num_tasks_blocking_shutdown_, 0U);
- --num_tasks_blocking_shutdown_;
- if (num_tasks_blocking_shutdown_ == 0 && shutdown_cv_)
- shutdown_cv_->Signal();
+ const bool shutdown_started_and_no_tasks_block_shutdown =
+ state_->DecrementNumTasksBlockingShutdown();
+ if (shutdown_started_and_no_tasks_block_shutdown)
+ OnBlockingShutdownTasksComplete();
}
}
+void TaskTracker::OnBlockingShutdownTasksComplete() {
+ AutoSchedulerLock auto_lock(lock_);
+
+ // This method can only be called after shutdown has started.
+ DCHECK(state_->ShutdownHasStarted());
+ DCHECK(shutdown_event_);
+
+ shutdown_event_->Signal();
+}
+
} // namespace internal
} // namespace base

Powered by Google App Engine
This is Rietveld 408576698