Index: base/task_scheduler/scheduler_worker_pool_impl.cc |
diff --git a/base/task_scheduler/scheduler_worker_pool_impl.cc b/base/task_scheduler/scheduler_worker_pool_impl.cc |
index 6b938ecae750e143c9d6aeac9b22706ddc20dcd7..ed8e84a336d6ac3015099e935e6ba2f0b6769598 100644 |
--- a/base/task_scheduler/scheduler_worker_pool_impl.cc |
+++ b/base/task_scheduler/scheduler_worker_pool_impl.cc |
@@ -34,6 +34,8 @@ namespace { |
constexpr char kPoolNameSuffix[] = "Pool"; |
constexpr char kDetachDurationHistogramPrefix[] = |
"TaskScheduler.DetachDuration."; |
+constexpr char kNumTasksBetweenWaitsHistogramPrefix[] = |
+ "TaskScheduler.NumTasksBetweenWaits."; |
constexpr char kTaskLatencyHistogramPrefix[] = "TaskScheduler.TaskLatency."; |
// SchedulerWorker that owns the current thread, if any. |
@@ -266,6 +268,17 @@ class SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl |
// Time when GetWork() first returned nullptr. |
TimeTicks idle_start_time_; |
+ // Indicates whether the last call to GetWork() returned nullptr. |
+ bool last_get_work_returned_nullptr_ = false; |
+ |
+ // Indicates whether the SchedulerWorker was detached since the last call to |
+ // GetWork(). |
+ bool did_detach_since_last_get_work_ = false; |
+ |
+ // Number of tasks executed since the last time the |
+ // TaskScheduler.NumTasksBetweenWaits histogram was recorded. |
+ size_t num_tasks_since_last_wait_ = 0; |
+ |
subtle::Atomic32 num_single_threaded_runners_ = 0; |
const int index_; |
@@ -470,8 +483,12 @@ void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnMainEntry( |
DCHECK(ContainsWorker(outer_->workers_, worker)); |
#endif |
- if (!detach_duration.is_max()) |
+ DCHECK_EQ(num_tasks_since_last_wait_, 0U); |
+ |
+ if (!detach_duration.is_max()) { |
outer_->detach_duration_histogram_->AddTime(detach_duration); |
+ did_detach_since_last_get_work_ = true; |
+ } |
PlatformThread::SetName( |
StringPrintf("TaskScheduler%sWorker%d", outer_->name_.c_str(), index_)); |
@@ -481,7 +498,7 @@ void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::OnMainEntry( |
tls_current_worker.Get().Set(worker); |
tls_current_worker_pool.Get().Set(outer_); |
- // New threads haven't run GetWork() yet, so reset the idle_start_time_. |
+ // New threads haven't run GetWork() yet, so reset the |idle_start_time_|. |
idle_start_time_ = TimeTicks(); |
ThreadRestrictions::SetIOAllowed( |
@@ -494,6 +511,24 @@ SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetWork( |
SchedulerWorker* worker) { |
DCHECK(ContainsWorker(outer_->workers_, worker)); |
+ // Record the TaskScheduler.NumTasksBetweenWaits histogram if the |
+ // SchedulerWorker waited on its WaitableEvent since the last GetWork(). |
+ // |
+ // Note: When GetWork() returns nullptr for the first time after returning a |
+ // Sequence, SchedulerWorker waits on its WaitableEvent. When the wait stops |
+ // (either because WakeUp() was called or because the sleep timeout expired), |
+ // GetWork() is called and the histogram is recorded. If GetWork() returns |
+ // nullptr again, the SchedulerWorker may detach. |
+ // |did_detach_since_last_get_work_| is set to true from OnMainEntry() if the |
+ // SchedulerWorker detaches and wakes up again. The next call to GetWork() |
+ // won't record the histogram (which is correct since the SchedulerWorker |
+ // didn't wait on its WaitableEvent since the last time the histogram was |
+ // recorded). |
+ if (last_get_work_returned_nullptr_ && !did_detach_since_last_get_work_) { |
+ outer_->num_tasks_between_waits_histogram_->Add(num_tasks_since_last_wait_); |
+ num_tasks_since_last_wait_ = 0; |
+ } |
+ |
scoped_refptr<Sequence> sequence; |
{ |
std::unique_ptr<PriorityQueue::Transaction> shared_transaction( |
@@ -520,6 +555,8 @@ SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetWork( |
outer_->AddToIdleWorkersStack(worker); |
if (idle_start_time_.is_null()) |
idle_start_time_ = TimeTicks::Now(); |
+ did_detach_since_last_get_work_ = false; |
+ last_get_work_returned_nullptr_ = true; |
return nullptr; |
} |
@@ -543,15 +580,19 @@ SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::GetWork( |
} |
DCHECK(sequence); |
+ outer_->RemoveFromIdleWorkersStack(worker); |
idle_start_time_ = TimeTicks(); |
+ did_detach_since_last_get_work_ = false; |
+ last_get_work_returned_nullptr_ = false; |
- outer_->RemoveFromIdleWorkersStack(worker); |
return sequence; |
} |
void SchedulerWorkerPoolImpl::SchedulerWorkerDelegateImpl::DidRunTask( |
const Task* task, |
const TimeDelta& task_latency) { |
+ ++num_tasks_since_last_wait_; |
+ |
const int priority_index = static_cast<int>(task->traits.priority()); |
// As explained in the header file, histograms are allocated on demand. It |
@@ -624,12 +665,23 @@ SchedulerWorkerPoolImpl::SchedulerWorkerPoolImpl( |
workers_created_(WaitableEvent::ResetPolicy::MANUAL, |
WaitableEvent::InitialState::NOT_SIGNALED), |
#endif |
+ // Mimics the UMA_HISTOGRAM_LONG_TIMES macro. |
detach_duration_histogram_(Histogram::FactoryTimeGet( |
kDetachDurationHistogramPrefix + name_ + kPoolNameSuffix, |
TimeDelta::FromMilliseconds(1), |
TimeDelta::FromHours(1), |
50, |
HistogramBase::kUmaTargetedHistogramFlag)), |
+ // Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A SchedulerWorker is |
+ // expected to run between zero and a few tens of tasks between waits. |
+ // When it runs more than 100 tasks, there is no need to know the exact |
+ // number of tasks that ran. |
+ num_tasks_between_waits_histogram_(Histogram::FactoryGet( |
+ kNumTasksBetweenWaitsHistogramPrefix + name_ + kPoolNameSuffix, |
+ 1, |
+ 100, |
+ 50, |
+ HistogramBase::kUmaTargetedHistogramFlag)), |
task_tracker_(task_tracker), |
delayed_task_manager_(delayed_task_manager) { |
DCHECK(task_tracker_); |