// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/sharing/nearby/platform/submittable_executor.h"
#include "base/functional/bind.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_restrictions.h"
namespace nearby {
namespace chrome {
SubmittableExecutor::SubmittableExecutor(
scoped_refptr<base::TaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
SubmittableExecutor::~SubmittableExecutor() {
{
base::AutoLock al(lock_);
is_shut_down_ = true;
if (num_incomplete_tasks_ == 0)
last_task_completed_.Signal();
}
// Block until all pending tasks are finished.
last_task_completed_.Wait();
// Grab the lock to ensure that RunTask() has returned.
base::AutoLock al(lock_);
CHECK_EQ(num_incomplete_tasks_, 0);
}
// Once called, this method will prevent any future calls to Submit() or
// Execute() from posting additional tasks. Previously posted asks will be
// allowed to complete normally.
void SubmittableExecutor::Shutdown() {
base::AutoLock al(lock_);
is_shut_down_ = true;
}
// Posts the given |runnable| and returns true immediately. If Shutdown() has
// been called, this method will return false.
bool SubmittableExecutor::DoSubmit(Runnable&& runnable) {
base::AutoLock al(lock_);
if (is_shut_down_)
return false;
++num_incomplete_tasks_;
return task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SubmittableExecutor::RunTask,
base::Unretained(this), std::move(runnable)));
}
// Posts the given |runnable| and returns immediately. If Shutdown() has been
// called, this method will do nothing.
void SubmittableExecutor::Execute(Runnable&& runnable) {
base::AutoLock al(lock_);
if (is_shut_down_)
return;
++num_incomplete_tasks_;
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SubmittableExecutor::RunTask,
base::Unretained(this), std::move(runnable)));
}
void SubmittableExecutor::RunTask(Runnable&& runnable) {
{
// The Nearby Connections library relies on many long running thread tasks
// which do not meet the usual usage pattern of a task on the chrome thread
// pool (short lived and non-blocking). Without WILL_BLOCK we end up thread
// starving tasks when multiple clients are using Nearby Connections at the
// same time because the thread pool by default is not big enough. By
// scoping this task as WILL_BLOCK we are letting the thread pool know that
// it should allocate an additional thread so this task does not starve
// other tasks if it does block and/or is long lived. See b/185628066 for
// examples of this starvation happening before this change.
base::ScopedBlockingCall blocking_call{FROM_HERE,
base::BlockingType::WILL_BLOCK};
// base::ScopedAllowBaseSyncPrimitives is required as code inside the
// runnable uses blocking primitive, which lives outside Chrome.
base::ScopedAllowBaseSyncPrimitives allow_wait;
runnable();
}
base::AutoLock al(lock_);
CHECK_GE(num_incomplete_tasks_, 1);
if (--num_incomplete_tasks_ == 0 && is_shut_down_)
last_task_completed_.Signal();
}
} // namespace chrome
} // namespace nearby