Task Posting v3: migrate (Sequenced|Thread)TaskRunnerHandle
To migrate to v3 task posting API, this CL moves moves (Sequenced|Thread)TaskRunnerHandle::Get() to (Sequenced|SingleThread)TaskRunner::GetCurrentDefault() and related classes. For more detail on this migration, refer to https://docs.google.com/document/d/1tssusPykvx3g0gvbvU4HxGyn3MjJlIylnsH13-Tv6s4/edit#bookmark=id.1umyn7v5a4p The blink codebase's presubmits were substantially changed. This was done because the presubmit did not parse in-class identifiers, and so there was no way of banning inner classes/methods/identifiers of a class which is allowed. The reason this was needed for this CL is that base::(Sequenced|SingleThread)TaskRunners are permitted in blink, but we need to ban (Sequenced|SingleThread)TaskRunner::GetCurrentDefault() and [...]::CurrentDefaultHandle to ensure that tasks in blink remain properly labeled. To this end, a new type of identifier (in-class identifiers, such as nested::namespace::ClassName::EnumClassName) is parsed by the presubmit, and a new kind of rule can be added to the _CONFIG file to allow/deny them. They are parsed very similarly to the existing identifiers, but crucially, they are allowed by default, whereas ClassNames are denied by default. In addition, the tests for the presubmit were mostly rewritten to properly test whether the identifiers in the test are actually allowed/disallowed. Instead of simply testing to see whether an identifier is present in _CONFIG in the presubmit file, the presubmit's functions are called in the same way as happens during the presubmit to make sure the code being run actually works. Finally, it also tests that the identifiers tested are fully parsed (a handful of them weren't because they were either preceded by '::' or in-class) by the presubmit. The task runner handle classes still exist in the codebase, and no existing Chromium code has been modified, as semantics have been preserved despite the implementation change. The implementation migration will then take place in future CLs. Because the two APIs expose the same functionality, the codebase can be incrementally migrated. Bug: 1026641 Change-Id: I7ea7bb36f30ecd3cdefedcffd8bae16118b0f3d2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3804911 Reviewed-by: Francois Pierre Doray <fdoray@chromium.org> Commit-Queue: Sean Maher <spvw@chromium.org> Reviewed-by: Daniel Cheng <dcheng@chromium.org> Reviewed-by: Gabriel Charette <gab@chromium.org> Cr-Commit-Position: refs/heads/main@{#1050813}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
06a3ac4df4
commit
03efef1173
PRESUBMIT.py
base
BUILD.gn
task
sequenced_task_runner.ccsequenced_task_runner.hsequenced_task_runner_unittest.ccsingle_thread_task_runner.ccsingle_thread_task_runner.hsingle_thread_task_runner_unittest.cc
threading
docs
third_party/blink/tools/blinkpy/presubmit
@ -491,6 +491,15 @@ _BANNED_CPP_FUNCTIONS : Sequence[BanRule] = (
|
||||
False,
|
||||
(),
|
||||
),
|
||||
BanRule(
|
||||
r'/\b(?!(Sequenced|SingleThread))\w*TaskRunner::(GetCurrentDefault|CurrentDefaultHandle)',
|
||||
(
|
||||
'It is not allowed to call these methods from the subclasses ',
|
||||
'of Sequenced or SingleThread task runners.',
|
||||
),
|
||||
True,
|
||||
(),
|
||||
),
|
||||
BanRule(
|
||||
r'/(Time(|Delta|Ticks)|ThreadTicks)::FromInternalValue|ToInternalValue',
|
||||
(
|
||||
|
@ -797,6 +797,7 @@ mixed_component("base") {
|
||||
"task/simple_task_executor.h",
|
||||
"task/single_thread_task_executor.cc",
|
||||
"task/single_thread_task_executor.h",
|
||||
"task/single_thread_task_runner.cc",
|
||||
"task/single_thread_task_runner.h",
|
||||
"task/single_thread_task_runner_thread_mode.h",
|
||||
"task/task_executor.cc",
|
||||
@ -3332,6 +3333,7 @@ test("base_unittests") {
|
||||
"task/sequence_manager/work_queue_unittest.cc",
|
||||
"task/sequenced_task_runner_unittest.cc",
|
||||
"task/single_thread_task_executor_unittest.cc",
|
||||
"task/single_thread_task_runner_unittest.cc",
|
||||
"task/task_runner_unittest.cc",
|
||||
"task/task_runner_util_unittest.cc",
|
||||
"task/task_traits_extension_unittest.cc",
|
||||
|
@ -7,11 +7,25 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/task/default_delayed_task_handle_delegate.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
ThreadLocalPointer<SequencedTaskRunner::CurrentDefaultHandle>&
|
||||
CurrentDefaultHandleTls() {
|
||||
static NoDestructor<
|
||||
ThreadLocalPointer<SequencedTaskRunner::CurrentDefaultHandle>>
|
||||
instance;
|
||||
return *instance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool SequencedTaskRunner::PostNonNestableTask(const Location& from_here,
|
||||
OnceClosure task) {
|
||||
return PostNonNestableDelayedTask(from_here, std::move(task),
|
||||
@ -72,6 +86,36 @@ bool SequencedTaskRunner::PostDelayedTaskAt(
|
||||
: delayed_run_time - TimeTicks::Now());
|
||||
}
|
||||
|
||||
// static
|
||||
const scoped_refptr<SequencedTaskRunner>&
|
||||
SequencedTaskRunner::GetCurrentDefault() {
|
||||
const CurrentDefaultHandle* current_default = CurrentDefaultHandleTls().Get();
|
||||
CHECK(current_default)
|
||||
<< "Error: This caller requires a sequenced context (i.e. the current "
|
||||
"task needs to run from a SequencedTaskRunner). If you're in a test "
|
||||
"refer to //docs/threading_and_tasks_testing.md.";
|
||||
return current_default->task_runner_;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SequencedTaskRunner::HasCurrentDefault() {
|
||||
return !!CurrentDefaultHandleTls().Get();
|
||||
}
|
||||
|
||||
SequencedTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle(
|
||||
scoped_refptr<SequencedTaskRunner> task_runner)
|
||||
: task_runner_(std::move(task_runner)) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK(!SequencedTaskRunner::HasCurrentDefault());
|
||||
CurrentDefaultHandleTls().Set(this);
|
||||
}
|
||||
|
||||
SequencedTaskRunner::CurrentDefaultHandle::~CurrentDefaultHandle() {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(CurrentDefaultHandleTls().Get(), this);
|
||||
CurrentDefaultHandleTls().Set(nullptr);
|
||||
}
|
||||
|
||||
bool SequencedTaskRunner::DeleteOrReleaseSoonInternal(
|
||||
const Location& from_here,
|
||||
void (*deleter)(const void*),
|
||||
|
@ -151,6 +151,10 @@ class PostDelayedTaskPassKeyForTesting : public PostDelayedTaskPassKey {};
|
||||
// has a method Run() that runs each runnable task in FIFO order
|
||||
// that can be called from any thread, but only if another
|
||||
// (non-nested) Run() call isn't already happening.
|
||||
//
|
||||
// SequencedTaskRunner::GetCurrentDefault() can be used while running
|
||||
// a task to retrieve the default SequencedTaskRunner for the current
|
||||
// sequence.
|
||||
class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
|
||||
public:
|
||||
// The two PostNonNestable*Task methods below are like their
|
||||
@ -263,9 +267,59 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
|
||||
// the current thread.
|
||||
virtual bool RunsTasksInCurrentSequence() const = 0;
|
||||
|
||||
// Returns the default SequencedThreadTaskRunner for the current task. It
|
||||
// should only be called if HasCurrentDefault() returns true (see the comment
|
||||
// there for the requirements).
|
||||
//
|
||||
// It is "default" in the sense that if the current sequence multiplexes
|
||||
// multiple task queues (e.g. BrowserThread::UI), this will return the default
|
||||
// task queue. A caller that wants a specific task queue should obtain it
|
||||
// directly instead of going through this API.
|
||||
//
|
||||
// See
|
||||
// https://chromium.googlesource.com/chromium/src/+/master/docs/threading_and_tasks.md#Posting-to-the-Current-Virtual_Thread
|
||||
// for details
|
||||
[[nodiscard]] static const scoped_refptr<SequencedTaskRunner>&
|
||||
GetCurrentDefault();
|
||||
|
||||
// Returns true if one of the following conditions is fulfilled:
|
||||
// a) A SequencedTaskRunner has been assigned to the current thread by
|
||||
// instantiating a SequencedTaskRunner::CurrentDefaultHandle.
|
||||
// b) The current thread has a SingleThreadTaskRunner::CurrentDefaultHandle
|
||||
// (which includes any thread that runs a MessagePump).
|
||||
[[nodiscard]] static bool HasCurrentDefault();
|
||||
|
||||
class BASE_EXPORT CurrentDefaultHandle {
|
||||
public:
|
||||
// Binds `task_runner` to the current thread so that it is returned by
|
||||
// GetCurrentDefault() in the scope of the constructed
|
||||
// `CurrentDefaultHandle`.
|
||||
explicit CurrentDefaultHandle(
|
||||
scoped_refptr<SequencedTaskRunner> task_runner);
|
||||
|
||||
CurrentDefaultHandle(const CurrentDefaultHandle&) = delete;
|
||||
CurrentDefaultHandle& operator=(const CurrentDefaultHandle&) = delete;
|
||||
|
||||
~CurrentDefaultHandle();
|
||||
|
||||
private:
|
||||
friend class SequencedTaskRunner;
|
||||
friend class CurrentHandleOverride;
|
||||
|
||||
scoped_refptr<SequencedTaskRunner> task_runner_;
|
||||
};
|
||||
|
||||
protected:
|
||||
~SequencedTaskRunner() override = default;
|
||||
|
||||
// Helper to allow SingleThreadTaskRunner::CurrentDefaultHandle to double as a
|
||||
// SequencedTaskRunner::CurrentDefaultHandle.
|
||||
static void SetCurrentDefaultHandleTaskRunner(
|
||||
CurrentDefaultHandle& current_default,
|
||||
scoped_refptr<SequencedTaskRunner> task_runner) {
|
||||
current_default.task_runner_ = task_runner;
|
||||
}
|
||||
|
||||
private:
|
||||
bool DeleteOrReleaseSoonInternal(const Location& from_here,
|
||||
void (*deleter)(const void*),
|
||||
|
@ -7,16 +7,21 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/gtest_prod_util.h"
|
||||
#include "base/location.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/sequence_checker_impl.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/null_task_runner.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "base/test/test_mock_time_task_runner.h"
|
||||
#include "base/test/test_simple_task_runner.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace base {
|
||||
@ -31,11 +36,6 @@ class FlagOnDelete {
|
||||
FlagOnDelete(const FlagOnDelete&) = delete;
|
||||
FlagOnDelete& operator=(const FlagOnDelete&) = delete;
|
||||
|
||||
private:
|
||||
friend class DeleteHelper<FlagOnDelete>;
|
||||
FRIEND_TEST_ALL_PREFIXES(SequencedTaskRunnerTest,
|
||||
OnTaskRunnerDeleterTargetStoppedEarly);
|
||||
|
||||
~FlagOnDelete() {
|
||||
EXPECT_FALSE(*deleted_);
|
||||
*deleted_ = true;
|
||||
@ -43,6 +43,7 @@ class FlagOnDelete {
|
||||
EXPECT_TRUE(expected_deletion_sequence_->RunsTasksInCurrentSequence());
|
||||
}
|
||||
|
||||
private:
|
||||
raw_ptr<bool> deleted_;
|
||||
const scoped_refptr<SequencedTaskRunner> expected_deletion_sequence_;
|
||||
};
|
||||
@ -68,14 +69,17 @@ class SequencedTaskRunnerTest : public testing::Test {
|
||||
test::TaskEnvironment task_environment_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
using SequenceBoundUniquePtr =
|
||||
std::unique_ptr<FlagOnDelete, OnTaskRunnerDeleter>;
|
||||
|
||||
TEST_F(SequencedTaskRunnerTest, OnTaskRunnerDeleterOnMainThread) {
|
||||
bool deleted_on_main_thread = false;
|
||||
SequenceBoundUniquePtr ptr(
|
||||
new FlagOnDelete(&deleted_on_main_thread, ThreadTaskRunnerHandle::Get()),
|
||||
OnTaskRunnerDeleter(ThreadTaskRunnerHandle::Get()));
|
||||
new FlagOnDelete(&deleted_on_main_thread,
|
||||
SequencedTaskRunner::GetCurrentDefault()),
|
||||
OnTaskRunnerDeleter(SequencedTaskRunner::GetCurrentDefault()));
|
||||
EXPECT_FALSE(deleted_on_main_thread);
|
||||
foreign_runner_->PostTask(
|
||||
FROM_HERE, BindOnce([](SequenceBoundUniquePtr) {}, std::move(ptr)));
|
||||
@ -91,8 +95,8 @@ TEST_F(SequencedTaskRunnerTest, OnTaskRunnerDeleterOnMainThread) {
|
||||
|
||||
TEST_F(SequencedTaskRunnerTest, OnTaskRunnerDeleterTargetStoppedEarly) {
|
||||
bool deleted_on_main_thread = false;
|
||||
FlagOnDelete* raw =
|
||||
new FlagOnDelete(&deleted_on_main_thread, ThreadTaskRunnerHandle::Get());
|
||||
FlagOnDelete* raw = new FlagOnDelete(
|
||||
&deleted_on_main_thread, SequencedTaskRunner::GetCurrentDefault());
|
||||
SequenceBoundUniquePtr ptr(raw, OnTaskRunnerDeleter(foreign_runner_));
|
||||
EXPECT_FALSE(deleted_on_main_thread);
|
||||
|
||||
@ -177,5 +181,74 @@ TEST_F(SequencedTaskRunnerTest, DelayedTaskHandle_PostTaskFailed) {
|
||||
EXPECT_FALSE(task_ran);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Tests for the SequencedTaskRunner::CurrentDefaultHandle machinery.
|
||||
class SequencedTaskRunnerCurrentDefaultHandleTest : public ::testing::Test {
|
||||
protected:
|
||||
// Verifies that the context it runs on has a
|
||||
// SequencedTaskRunner::CurrentDefaultHandle and that posting to it results in
|
||||
// the posted task running in that same context (sequence).
|
||||
static void VerifyCurrentSequencedTaskRunner() {
|
||||
ASSERT_TRUE(SequencedTaskRunner::HasCurrentDefault());
|
||||
scoped_refptr<SequencedTaskRunner> task_runner =
|
||||
SequencedTaskRunner::GetCurrentDefault();
|
||||
ASSERT_TRUE(task_runner);
|
||||
|
||||
// Use SequenceCheckerImpl to make sure it's not a no-op in Release builds.
|
||||
std::unique_ptr<SequenceCheckerImpl> sequence_checker =
|
||||
std::make_unique<SequenceCheckerImpl>();
|
||||
task_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
&SequencedTaskRunnerCurrentDefaultHandleTest::CheckValidSequence,
|
||||
std::move(sequence_checker)));
|
||||
}
|
||||
|
||||
static void CheckValidSequence(
|
||||
std::unique_ptr<SequenceCheckerImpl> sequence_checker) {
|
||||
EXPECT_TRUE(sequence_checker->CalledOnValidSequence());
|
||||
}
|
||||
|
||||
test::TaskEnvironment task_environment_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(SequencedTaskRunnerCurrentDefaultHandleTest, FromTaskEnvironment) {
|
||||
VerifyCurrentSequencedTaskRunner();
|
||||
RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST_F(SequencedTaskRunnerCurrentDefaultHandleTest,
|
||||
FromThreadPoolSequencedTask) {
|
||||
base::ThreadPool::CreateSequencedTaskRunner({})->PostTask(
|
||||
FROM_HERE, base::BindOnce(&SequencedTaskRunnerCurrentDefaultHandleTest::
|
||||
VerifyCurrentSequencedTaskRunner));
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST_F(SequencedTaskRunnerCurrentDefaultHandleTest,
|
||||
NoHandleFromUnsequencedTask) {
|
||||
base::ThreadPool::PostTask(base::BindOnce(
|
||||
[]() { EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault()); }));
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST(SequencedTaskRunnerCurrentDefaultHandleTestWithoutTaskEnvironment,
|
||||
FromHandleInScope) {
|
||||
scoped_refptr<SequencedTaskRunner> test_task_runner =
|
||||
MakeRefCounted<TestSimpleTaskRunner>();
|
||||
EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault());
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
{
|
||||
SequencedTaskRunner::CurrentDefaultHandle current_default(test_task_runner);
|
||||
EXPECT_TRUE(SequencedTaskRunner::HasCurrentDefault());
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(test_task_runner, SequencedTaskRunner::GetCurrentDefault());
|
||||
}
|
||||
EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault());
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
129
base/task/single_thread_task_runner.cc
Normal file
129
base/task/single_thread_task_runner.cc
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/dcheck_is_on.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
ThreadLocalPointer<SingleThreadTaskRunner::CurrentDefaultHandle>&
|
||||
CurrentDefaultHandleTls() {
|
||||
static NoDestructor<
|
||||
ThreadLocalPointer<SingleThreadTaskRunner::CurrentDefaultHandle>>
|
||||
instance;
|
||||
return *instance;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
const scoped_refptr<SingleThreadTaskRunner>&
|
||||
SingleThreadTaskRunner::GetCurrentDefault() {
|
||||
const SingleThreadTaskRunner::CurrentDefaultHandle* current_default =
|
||||
CurrentDefaultHandleTls().Get();
|
||||
CHECK(current_default)
|
||||
<< "Error: This caller requires a single-threaded context (i.e. the "
|
||||
"current task needs to run from a SingleThreadTaskRunner). If you're "
|
||||
"in a test refer to //docs/threading_and_tasks_testing.md."
|
||||
<< (SequencedTaskRunner::HasCurrentDefault()
|
||||
? " Note: base::SequencedTaskRunner::GetCurrentDefault() "
|
||||
"is set; "
|
||||
"consider using it if the current task can run from a "
|
||||
"SequencedTaskRunner."
|
||||
: "");
|
||||
return current_default->task_runner_;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SingleThreadTaskRunner::HasCurrentDefault() {
|
||||
return !!CurrentDefaultHandleTls().Get();
|
||||
}
|
||||
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle(
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner)
|
||||
: task_runner_(std::move(task_runner)),
|
||||
sequenced_task_runner_current_default_(task_runner_) {
|
||||
DCHECK(task_runner_->BelongsToCurrentThread());
|
||||
DCHECK(!CurrentDefaultHandleTls().Get());
|
||||
CurrentDefaultHandleTls().Set(this);
|
||||
}
|
||||
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle::~CurrentDefaultHandle() {
|
||||
DCHECK(task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_EQ(CurrentDefaultHandleTls().Get(), this);
|
||||
CurrentDefaultHandleTls().Set(nullptr);
|
||||
}
|
||||
|
||||
SingleThreadTaskRunner::CurrentHandleOverride::CurrentHandleOverride(
|
||||
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner,
|
||||
bool allow_nested_runloop) {
|
||||
DCHECK(!SequencedTaskRunnerHandle::IsSet() || ThreadTaskRunnerHandle::IsSet())
|
||||
<< "SingleThreadTaskRunner::CurrentHandleOverride is not compatible with "
|
||||
"a SequencedTaskRunner::CurrentDefaultHandle already "
|
||||
"being set on this thread (except when it's "
|
||||
"set by the current "
|
||||
"SingleThreadTaskRunner::CurrentDefaultHandle).";
|
||||
|
||||
if (!ThreadTaskRunnerHandle::IsSet()) {
|
||||
top_level_thread_task_runner_current_default_.emplace(
|
||||
std::move(overriding_task_runner));
|
||||
return;
|
||||
}
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
expected_task_runner_before_restore_ = overriding_task_runner.get();
|
||||
#endif
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle* current_default =
|
||||
CurrentDefaultHandleTls().Get();
|
||||
SequencedTaskRunner::SetCurrentDefaultHandleTaskRunner(
|
||||
current_default->sequenced_task_runner_current_default_,
|
||||
overriding_task_runner);
|
||||
current_default->task_runner_.swap(overriding_task_runner);
|
||||
// Due to the swap, now `current_default->task_runner_` points to the
|
||||
// overriding task runner and `overriding_task_runner_` points to the previous
|
||||
// task runner.
|
||||
task_runner_to_restore_ = std::move(overriding_task_runner);
|
||||
|
||||
if (!allow_nested_runloop) {
|
||||
no_running_during_override_ =
|
||||
std::make_unique<ScopedDisallowRunningRunLoop>();
|
||||
}
|
||||
}
|
||||
|
||||
SingleThreadTaskRunner::CurrentHandleOverride::~CurrentHandleOverride() {
|
||||
if (task_runner_to_restore_) {
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle* current_default =
|
||||
CurrentDefaultHandleTls().Get();
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
DCHECK_EQ(expected_task_runner_before_restore_,
|
||||
current_default->task_runner_.get())
|
||||
<< "Nested overrides must expire their "
|
||||
"SingleThreadTaskRunner::CurrentHandleOverride "
|
||||
"in LIFO order.";
|
||||
#endif
|
||||
|
||||
SequencedTaskRunner::SetCurrentDefaultHandleTaskRunner(
|
||||
current_default->sequenced_task_runner_current_default_,
|
||||
task_runner_to_restore_);
|
||||
current_default->task_runner_.swap(task_runner_to_restore_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
@ -6,10 +6,18 @@
|
||||
#define BASE_TASK_SINGLE_THREAD_TASK_RUNNER_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/dcheck_is_on.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace blink::scheduler {
|
||||
class MainThreadSchedulerImpl;
|
||||
} // namespace blink::scheduler
|
||||
|
||||
namespace base {
|
||||
|
||||
class ScopedDisallowRunningRunLoop;
|
||||
|
||||
// A SingleThreadTaskRunner is a SequencedTaskRunner with one more
|
||||
// guarantee; namely, that all tasks are run on a single dedicated
|
||||
// thread. Most use cases require only a SequencedTaskRunner, unless
|
||||
@ -27,6 +35,129 @@ class BASE_EXPORT SingleThreadTaskRunner : public SequencedTaskRunner {
|
||||
// A more explicit alias to RunsTasksInCurrentSequence().
|
||||
bool BelongsToCurrentThread() const { return RunsTasksInCurrentSequence(); }
|
||||
|
||||
// Returns the default SingleThreadTaskRunner for the current thread.
|
||||
// On threads that service multiple task queues, the default task queue is
|
||||
// preferred to inheriting the current task queue (otherwise, everything would
|
||||
// implicitly be "input priority"...). If the caller knows which task queue it
|
||||
// should be running on, it should post to that SingleThreadTaskRunner
|
||||
// directly instead of GetCurrentDefault(). This is critical in some
|
||||
// cases, e.g. DeleteSoon or RefCountedDeleteOnSequence should delete the
|
||||
// object on the same task queue it's used from (or on a lower priority).
|
||||
//
|
||||
// DCHECKs if the current thread isn't servicing a SingleThreadTaskRunner.
|
||||
//
|
||||
// See
|
||||
// https://chromium.googlesource.com/chromium/src/+/master/docs/threading_and_tasks.md#Posting-to-the-Current-Virtual_Thread
|
||||
// for details
|
||||
|
||||
[[nodiscard]] static const scoped_refptr<SingleThreadTaskRunner>&
|
||||
GetCurrentDefault();
|
||||
|
||||
// Returns true if the SingleThreadTaskRunner is already created for
|
||||
// the current thread.
|
||||
[[nodiscard]] static bool HasCurrentDefault();
|
||||
|
||||
class CurrentHandleOverride;
|
||||
class CurrentHandleOverrideForTesting;
|
||||
|
||||
class BASE_EXPORT CurrentDefaultHandle {
|
||||
public:
|
||||
// Binds |task_runner| to the current thread. |task_runner| must belong
|
||||
// to the current thread.
|
||||
explicit CurrentDefaultHandle(
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner);
|
||||
|
||||
CurrentDefaultHandle(const CurrentDefaultHandle&) = delete;
|
||||
CurrentDefaultHandle& operator=(const CurrentDefaultHandle&) = delete;
|
||||
|
||||
~CurrentDefaultHandle();
|
||||
|
||||
private:
|
||||
friend class SingleThreadTaskRunner;
|
||||
friend class CurrentHandleOverride;
|
||||
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_;
|
||||
|
||||
// Registers |task_runner_|'s SequencedTaskRunner interface as the
|
||||
// SequencedTaskRunner::CurrentDefaultHandle on this thread.
|
||||
SequencedTaskRunner::CurrentDefaultHandle
|
||||
sequenced_task_runner_current_default_;
|
||||
};
|
||||
|
||||
// CurrentHandleOverride overrides the task runner returned by
|
||||
// |SingleThreadTaskRunner::GetCurrentDefault()| to point at
|
||||
// |overriding_task_runner| until the |CurrentHandleOverride| goes out of
|
||||
// scope. CurrentHandleOverride instantiates a new SingleThreadTaskRunner if
|
||||
// SingleThreadTaskRunner is not instantiated on the current thread. Nested
|
||||
// overrides are allowed but callers must ensure the |CurrentHandleOverride|s
|
||||
// expire in LIFO (stack) order.
|
||||
//
|
||||
// Note: nesting SingleThreadTaskRunner is subtle and should be done with
|
||||
// care, hence the need to friend and request a //base/OWNERS review for usage
|
||||
// outside of tests. Use CurrentHandleOverrideForTesting to bypass the friend
|
||||
// requirement in tests.
|
||||
class BASE_EXPORT CurrentHandleOverride {
|
||||
public:
|
||||
CurrentHandleOverride(const CurrentHandleOverride&) = delete;
|
||||
CurrentHandleOverride& operator=(const CurrentHandleOverride&) = delete;
|
||||
~CurrentHandleOverride();
|
||||
|
||||
private:
|
||||
friend class CurrentHandleOverrideForTesting;
|
||||
FRIEND_TEST_ALL_PREFIXES(SingleThreadTaskRunnerCurrentDefaultHandleTest,
|
||||
NestedRunLoop);
|
||||
|
||||
// This is in order for ThreadTaskRunnerHandleOverride to call this private
|
||||
// constructor during migration.
|
||||
friend class ThreadTaskRunnerHandleOverride;
|
||||
|
||||
// We expect SingleThreadTaskRunner::CurrentHandleOverride to be only needed
|
||||
// under special circumstances. Require them to be enumerated as friends to
|
||||
// require //base/OWNERS review. Use
|
||||
// SingleTaskRunner::CurrentHandleOverrideForTesting in unit tests to avoid
|
||||
// the friend requirement.
|
||||
|
||||
friend class blink::scheduler::MainThreadSchedulerImpl;
|
||||
|
||||
// Constructs a SingleThreadTaskRunner::CurrentHandleOverride which will
|
||||
// make SingleThreadTaskRunner::GetCurrentDefault() return
|
||||
// |overriding_task_runner| for its lifetime. |allow_nested_loop| specifies
|
||||
// whether RunLoop::Run() is allowed during this override's lifetime. It's
|
||||
// not recommended to allow this unless the current thread's scheduler
|
||||
// guarantees that only tasks which pertain to |overriding_task_runner|'s
|
||||
// context will be run by nested RunLoops.
|
||||
explicit CurrentHandleOverride(
|
||||
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner,
|
||||
bool allow_nested_runloop = false);
|
||||
|
||||
absl::optional<SingleThreadTaskRunner::CurrentDefaultHandle>
|
||||
top_level_thread_task_runner_current_default_;
|
||||
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_to_restore_;
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
SingleThreadTaskRunner* expected_task_runner_before_restore_{nullptr};
|
||||
#endif
|
||||
|
||||
std::unique_ptr<ScopedDisallowRunningRunLoop> no_running_during_override_;
|
||||
};
|
||||
|
||||
// Note: nesting CurrentHandleOverrides isn't generally desired but it's
|
||||
// useful in some unit tests where multiple task runners share the main thread
|
||||
// for simplicity and determinism. Only use this when no other constructs will
|
||||
// work (see base/test/task_environment.h and
|
||||
// base/test/test_mock_time_task_runner.h for preferred alternatives).
|
||||
class BASE_EXPORT CurrentHandleOverrideForTesting {
|
||||
public:
|
||||
explicit CurrentHandleOverrideForTesting(
|
||||
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner)
|
||||
: thread_task_runner_current_override_(
|
||||
std::move(overriding_task_runner)) {}
|
||||
|
||||
private:
|
||||
CurrentHandleOverride thread_task_runner_current_override_;
|
||||
};
|
||||
|
||||
protected:
|
||||
~SingleThreadTaskRunner() override = default;
|
||||
};
|
||||
|
166
base/task/single_thread_task_runner_unittest.cc
Normal file
166
base/task/single_thread_task_runner_unittest.cc
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "base/test/gtest_util.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "base/test/test_simple_task_runner.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
TEST(SingleThreadTaskRunnerCurrentDefaultHandleTest, Basic) {
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
{
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle sttcd1(task_runner);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
}
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
}
|
||||
|
||||
TEST(SingleThreadTaskRunnerCurrentDefaultHandleTest, DeathOnImplicitOverride) {
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
scoped_refptr<SingleThreadTaskRunner> overidding_task_runner(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle sttcd(task_runner);
|
||||
EXPECT_DCHECK_DEATH({
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle overriding_sttcd(
|
||||
overidding_task_runner);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(SingleThreadTaskRunnerCurrentDefaultHandleTest, OverrideExistingSTTCD) {
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_1(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_2(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_3(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_4(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
{
|
||||
// STTCD in place prior to override.
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle sttcd1(task_runner_1);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_1, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
|
||||
{
|
||||
// Override.
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting sttcd_override_2(
|
||||
task_runner_2);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_2, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
|
||||
{
|
||||
// Nested override.
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting
|
||||
sttcd_override_3(task_runner_3);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_3, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
}
|
||||
|
||||
// Back to single override.
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_2, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
|
||||
{
|
||||
// Backup to double override with another STTCD.
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting
|
||||
sttcd_override_4(task_runner_4);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_4, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
}
|
||||
}
|
||||
|
||||
// Back to simple STTCD.
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_1, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
}
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
}
|
||||
|
||||
TEST(SingleThreadTaskRunnerCurrentDefaultHandleTest, OverrideNoExistingSTTCD) {
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_1(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_2(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
{
|
||||
// Override with no STTCD in place.
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting sttcd_override_1(
|
||||
task_runner_1);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_1, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
|
||||
{
|
||||
// Nested override works the same.
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting sttcd_override_2(
|
||||
task_runner_2);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_2, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
}
|
||||
|
||||
// Back to single override.
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner_1, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
}
|
||||
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
}
|
||||
|
||||
TEST(SingleThreadTaskRunnerCurrentDefaultHandleTest, DeathOnSTTCDOverOverride) {
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
scoped_refptr<SingleThreadTaskRunner> overidding_task_runner(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting sttcd_override(
|
||||
task_runner);
|
||||
EXPECT_DCHECK_DEATH({
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle overriding_sttrcd(
|
||||
overidding_task_runner);
|
||||
});
|
||||
}
|
||||
|
||||
TEST(SingleThreadTaskRunnerCurrentDefaultHandleTest, NestedRunLoop) {
|
||||
test::SingleThreadTaskEnvironment task_environment;
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
SingleThreadTaskRunner::CurrentHandleOverride sttrcd_override(
|
||||
task_runner,
|
||||
/*allow_nested_runloop=*/true);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
EXPECT_EQ(task_runner, SequencedTaskRunner::GetCurrentDefault());
|
||||
RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST(SingleThreadTaskRunnerCurrentDefaultHandleTest, DeathOnNestedRunLoop) {
|
||||
test::SingleThreadTaskEnvironment task_environment;
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner(
|
||||
MakeRefCounted<TestSimpleTaskRunner>());
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting sttcd_override(
|
||||
task_runner);
|
||||
EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault());
|
||||
EXPECT_EQ(task_runner, SingleThreadTaskRunner::GetCurrentDefault());
|
||||
EXPECT_EQ(task_runner, SequencedTaskRunner::GetCurrentDefault());
|
||||
EXPECT_DCHECK_DEATH({ RunLoop().RunUntilIdle(); });
|
||||
}
|
||||
|
||||
} // namespace base
|
@ -4,49 +4,18 @@
|
||||
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
LazyInstance<ThreadLocalPointer<SequencedTaskRunnerHandle>>::Leaky
|
||||
sequenced_task_runner_tls = LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
} // namespace
|
||||
|
||||
// DEPRECATED: Use SequencedTaskRunner::GetCurrentDefault instead
|
||||
// static
|
||||
const scoped_refptr<SequencedTaskRunner>& SequencedTaskRunnerHandle::Get() {
|
||||
const SequencedTaskRunnerHandle* current =
|
||||
sequenced_task_runner_tls.Pointer()->Get();
|
||||
CHECK(current)
|
||||
<< "Error: This caller requires a sequenced context (i.e. the current "
|
||||
"task needs to run from a SequencedTaskRunner). If you're in a test "
|
||||
"refer to //docs/threading_and_tasks_testing.md.";
|
||||
return current->task_runner_;
|
||||
return SequencedTaskRunner::GetCurrentDefault();
|
||||
}
|
||||
|
||||
// DEPRECATED: Use SequencedTaskRunner::HasCurrentDefault instead
|
||||
// static
|
||||
bool SequencedTaskRunnerHandle::IsSet() {
|
||||
return !!sequenced_task_runner_tls.Pointer()->Get();
|
||||
}
|
||||
|
||||
SequencedTaskRunnerHandle::SequencedTaskRunnerHandle(
|
||||
scoped_refptr<SequencedTaskRunner> task_runner)
|
||||
: task_runner_(std::move(task_runner)) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK(!SequencedTaskRunnerHandle::IsSet());
|
||||
sequenced_task_runner_tls.Pointer()->Set(this);
|
||||
}
|
||||
|
||||
SequencedTaskRunnerHandle::~SequencedTaskRunnerHandle() {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(sequenced_task_runner_tls.Pointer()->Get(), this);
|
||||
sequenced_task_runner_tls.Pointer()->Set(nullptr);
|
||||
return SequencedTaskRunner::HasCurrentDefault();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
@ -15,12 +15,14 @@ class ThreadTaskRunnerHandle;
|
||||
|
||||
class BASE_EXPORT SequencedTaskRunnerHandle {
|
||||
public:
|
||||
// DEPRECATED: Use SequencedTaskRunner::GetCurrentDefault() instead.
|
||||
// Returns a SequencedTaskRunner which guarantees that posted tasks will only
|
||||
// run after the current task is finished and will satisfy a SequenceChecker.
|
||||
// It should only be called if IsSet() returns true (see the comment there for
|
||||
// the requirements).
|
||||
[[nodiscard]] static const scoped_refptr<SequencedTaskRunner>& Get();
|
||||
|
||||
// DEPRECATED: Use SequencedTaskRunner::HasCurrentDefault() instead.
|
||||
// Returns true if one of the following conditions is fulfilled:
|
||||
// a) A SequencedTaskRunner has been assigned to the current thread by
|
||||
// instantiating a SequencedTaskRunnerHandle.
|
||||
@ -28,20 +30,18 @@ class BASE_EXPORT SequencedTaskRunnerHandle {
|
||||
// thread that has a MessageLoop associated with it).
|
||||
[[nodiscard]] static bool IsSet();
|
||||
|
||||
// Binds |task_runner| to the current thread.
|
||||
explicit SequencedTaskRunnerHandle(
|
||||
scoped_refptr<SequencedTaskRunner> task_runner);
|
||||
scoped_refptr<SequencedTaskRunner> task_runner)
|
||||
: contained_current_default_(std::move(task_runner)) {}
|
||||
|
||||
SequencedTaskRunnerHandle(const SequencedTaskRunnerHandle&) = delete;
|
||||
SequencedTaskRunnerHandle& operator=(const SequencedTaskRunnerHandle&) =
|
||||
delete;
|
||||
|
||||
~SequencedTaskRunnerHandle();
|
||||
~SequencedTaskRunnerHandle() = default;
|
||||
|
||||
private:
|
||||
friend class ThreadTaskRunnerHandleOverride;
|
||||
|
||||
scoped_refptr<SequencedTaskRunner> task_runner_;
|
||||
SequencedTaskRunner::CurrentDefaultHandle contained_current_default_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
@ -4,105 +4,18 @@
|
||||
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/dcheck_is_on.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
base::LazyInstance<base::ThreadLocalPointer<ThreadTaskRunnerHandle>>::Leaky
|
||||
thread_task_runner_tls = LAZY_INSTANCE_INITIALIZER;
|
||||
|
||||
} // namespace
|
||||
|
||||
// DEPRECATED: Use SequencedTaskRunner::GetCurrentDefault instead
|
||||
// static
|
||||
const scoped_refptr<SingleThreadTaskRunner>& ThreadTaskRunnerHandle::Get() {
|
||||
const ThreadTaskRunnerHandle* current =
|
||||
thread_task_runner_tls.Pointer()->Get();
|
||||
CHECK(current)
|
||||
<< "Error: This caller requires a single-threaded context (i.e. the "
|
||||
"current task needs to run from a SingleThreadTaskRunner). If you're "
|
||||
"in a test refer to //docs/threading_and_tasks_testing.md."
|
||||
<< (SequencedTaskRunnerHandle::IsSet()
|
||||
? " Note: base::SequencedTaskRunnerHandle::Get() is set; "
|
||||
"consider using it if the current task can run from a "
|
||||
"SequencedTaskRunner."
|
||||
: "");
|
||||
return current->task_runner_;
|
||||
return SingleThreadTaskRunner::GetCurrentDefault();
|
||||
}
|
||||
|
||||
// DEPRECATED: Use SequencedTaskRunner::HasCurrentDefault instead
|
||||
// static
|
||||
bool ThreadTaskRunnerHandle::IsSet() {
|
||||
return !!thread_task_runner_tls.Pointer()->Get();
|
||||
}
|
||||
|
||||
ThreadTaskRunnerHandle::ThreadTaskRunnerHandle(
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner)
|
||||
: task_runner_(std::move(task_runner)),
|
||||
sequenced_task_runner_handle_(task_runner_) {
|
||||
DCHECK(task_runner_->BelongsToCurrentThread());
|
||||
DCHECK(!thread_task_runner_tls.Pointer()->Get());
|
||||
thread_task_runner_tls.Pointer()->Set(this);
|
||||
}
|
||||
|
||||
ThreadTaskRunnerHandle::~ThreadTaskRunnerHandle() {
|
||||
DCHECK(task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_EQ(thread_task_runner_tls.Pointer()->Get(), this);
|
||||
thread_task_runner_tls.Pointer()->Set(nullptr);
|
||||
}
|
||||
|
||||
ThreadTaskRunnerHandleOverride::ThreadTaskRunnerHandleOverride(
|
||||
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner,
|
||||
bool allow_nested_runloop) {
|
||||
DCHECK(!SequencedTaskRunnerHandle::IsSet() || ThreadTaskRunnerHandle::IsSet())
|
||||
<< "ThreadTaskRunnerHandleOverride is not compatible with a "
|
||||
"SequencedTaskRunnerHandle already being set on this thread (except "
|
||||
"when it's set by the current ThreadTaskRunnerHandle).";
|
||||
|
||||
if (!ThreadTaskRunnerHandle::IsSet()) {
|
||||
top_level_thread_task_runner_handle_.emplace(
|
||||
std::move(overriding_task_runner));
|
||||
return;
|
||||
}
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
expected_task_runner_before_restore_ = overriding_task_runner.get();
|
||||
#endif
|
||||
ThreadTaskRunnerHandle* ttrh = thread_task_runner_tls.Pointer()->Get();
|
||||
ttrh->sequenced_task_runner_handle_.task_runner_ = overriding_task_runner;
|
||||
ttrh->task_runner_.swap(overriding_task_runner);
|
||||
// Due to the swap, now `ttrh->task_runner_` points to the overriding task
|
||||
// runner and `overriding_task_runner_` points to the previous task runner.
|
||||
task_runner_to_restore_ = std::move(overriding_task_runner);
|
||||
|
||||
if (!allow_nested_runloop) {
|
||||
no_running_during_override_ =
|
||||
std::make_unique<ScopedDisallowRunningRunLoop>();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadTaskRunnerHandleOverride::~ThreadTaskRunnerHandleOverride() {
|
||||
if (task_runner_to_restore_) {
|
||||
ThreadTaskRunnerHandle* ttrh = thread_task_runner_tls.Pointer()->Get();
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
DCHECK_EQ(expected_task_runner_before_restore_, ttrh->task_runner_.get())
|
||||
<< "Nested overrides must expire their ThreadTaskRunnerHandleOverride "
|
||||
"in LIFO order.";
|
||||
#endif
|
||||
|
||||
ttrh->sequenced_task_runner_handle_.task_runner_ = task_runner_to_restore_;
|
||||
ttrh->task_runner_.swap(task_runner_to_restore_);
|
||||
}
|
||||
return SingleThreadTaskRunner::HasCurrentDefault();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
@ -5,8 +5,6 @@
|
||||
#ifndef BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_
|
||||
#define BASE_THREADING_THREAD_TASK_RUNNER_HANDLE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/dcheck_is_on.h"
|
||||
#include "base/gtest_prod_util.h"
|
||||
@ -15,16 +13,8 @@
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace blink {
|
||||
namespace scheduler {
|
||||
class MainThreadSchedulerImpl;
|
||||
} // namespace scheduler
|
||||
} // namespace blink
|
||||
|
||||
namespace base {
|
||||
|
||||
class ScopedDisallowRunningRunLoop;
|
||||
|
||||
// ThreadTaskRunnerHandle stores a reference to a thread's TaskRunner
|
||||
// in thread-local storage. Callers can then retrieve the TaskRunner
|
||||
// for the current thread by calling ThreadTaskRunnerHandle::Get().
|
||||
@ -32,9 +22,11 @@ class ScopedDisallowRunningRunLoop;
|
||||
// Prefer SequencedTaskRunnerHandle to this unless thread affinity is required.
|
||||
class BASE_EXPORT ThreadTaskRunnerHandle {
|
||||
public:
|
||||
// DEPRECATED: use SingleThreadTaskRunner::GetCurrentDefault instead
|
||||
// Gets the SingleThreadTaskRunner for the current thread.
|
||||
[[nodiscard]] static const scoped_refptr<SingleThreadTaskRunner>& Get();
|
||||
|
||||
// DEPRECATED: Use SingleThreadTaskRunner::HasCurrentDefault
|
||||
// Returns true if the SingleThreadTaskRunner is already created for
|
||||
// the current thread.
|
||||
[[nodiscard]] static bool IsSet();
|
||||
@ -42,22 +34,20 @@ class BASE_EXPORT ThreadTaskRunnerHandle {
|
||||
// Binds |task_runner| to the current thread. |task_runner| must belong
|
||||
// to the current thread for this to succeed.
|
||||
explicit ThreadTaskRunnerHandle(
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner);
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner)
|
||||
: contained_current_default_(std::move(task_runner)) {}
|
||||
|
||||
ThreadTaskRunnerHandle(const ThreadTaskRunnerHandle&) = delete;
|
||||
ThreadTaskRunnerHandle& operator=(const ThreadTaskRunnerHandle&) = delete;
|
||||
|
||||
~ThreadTaskRunnerHandle();
|
||||
~ThreadTaskRunnerHandle() = default;
|
||||
|
||||
private:
|
||||
friend class ThreadTaskRunnerHandleOverride;
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_;
|
||||
|
||||
// Registers |task_runner_|'s SequencedTaskRunner interface as the
|
||||
// SequencedTaskRunnerHandle on this thread.
|
||||
SequencedTaskRunnerHandle sequenced_task_runner_handle_;
|
||||
SingleThreadTaskRunner::CurrentDefaultHandle contained_current_default_;
|
||||
};
|
||||
|
||||
// DEPRECATED: Use SingleThreadTaskRunner::CurrentHandleOverride instead.
|
||||
//
|
||||
// ThreadTaskRunnerHandleOverride overrides the task runner returned by
|
||||
// |ThreadTaskRunnerHandle::Get()| to point at |overriding_task_runner| until
|
||||
// the |ThreadTaskRunnerHandleOverride| goes out of scope.
|
||||
@ -76,7 +66,7 @@ class BASE_EXPORT ThreadTaskRunnerHandleOverride {
|
||||
delete;
|
||||
ThreadTaskRunnerHandleOverride& operator=(
|
||||
const ThreadTaskRunnerHandleOverride&) = delete;
|
||||
~ThreadTaskRunnerHandleOverride();
|
||||
~ThreadTaskRunnerHandleOverride() = default;
|
||||
|
||||
private:
|
||||
friend class ThreadTaskRunnerHandleOverrideForTesting;
|
||||
@ -97,14 +87,11 @@ class BASE_EXPORT ThreadTaskRunnerHandleOverride {
|
||||
// |overriding_task_runner|'s context will be run by nested RunLoops.
|
||||
explicit ThreadTaskRunnerHandleOverride(
|
||||
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner,
|
||||
bool allow_nested_runloop = false);
|
||||
bool allow_nested_runloop = false)
|
||||
: contained_override_(std::move(overriding_task_runner),
|
||||
allow_nested_runloop) {}
|
||||
|
||||
absl::optional<ThreadTaskRunnerHandle> top_level_thread_task_runner_handle_;
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner_to_restore_;
|
||||
#if DCHECK_IS_ON()
|
||||
SingleThreadTaskRunner* expected_task_runner_before_restore_{nullptr};
|
||||
#endif
|
||||
std::unique_ptr<ScopedDisallowRunningRunLoop> no_running_during_override_;
|
||||
SingleThreadTaskRunner::CurrentHandleOverride contained_override_;
|
||||
};
|
||||
|
||||
// Note: nesting ThreadTaskRunnerHandles isn't generally desired but it's useful
|
||||
@ -116,11 +103,12 @@ class ThreadTaskRunnerHandleOverrideForTesting {
|
||||
public:
|
||||
explicit ThreadTaskRunnerHandleOverrideForTesting(
|
||||
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner)
|
||||
: thread_task_runner_handle_override_(std::move(overriding_task_runner)) {
|
||||
}
|
||||
: contained_override_(std::move(overriding_task_runner)) {}
|
||||
|
||||
~ThreadTaskRunnerHandleOverrideForTesting() = default;
|
||||
|
||||
private:
|
||||
ThreadTaskRunnerHandleOverride thread_task_runner_handle_override_;
|
||||
SingleThreadTaskRunner::CurrentHandleOverrideForTesting contained_override_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
@ -176,8 +176,8 @@ If you find yourself writing a sequence-friendly type and it fails
|
||||
thread-affinity checks (e.g., `THREAD_CHECKER`) in a leaf dependency: consider
|
||||
making that dependency sequence-friendly as well. Most core APIs in Chrome are
|
||||
sequence-friendly, but some legacy types may still over-zealously use
|
||||
ThreadChecker/ThreadTaskRunnerHandle/SingleThreadTaskRunner when they could
|
||||
instead rely on the "current sequence" and no longer be thread-affine.
|
||||
ThreadChecker/SingleThreadTaskRunner when they could instead rely on the
|
||||
"current sequence" and no longer be thread-affine.
|
||||
|
||||
## Posting a Parallel Task
|
||||
|
||||
@ -263,20 +263,21 @@ sequenced_task_runner->PostTask(FROM_HERE, base::BindOnce(&TaskB));
|
||||
### Posting to the Current (Virtual) Thread
|
||||
|
||||
The preferred way of posting to the current (virtual) thread is via
|
||||
`base::SequencedTaskRunnerHandle::Get()`.
|
||||
`base::SequencedTaskRunner::GetCurrentDefault()`.
|
||||
|
||||
```cpp
|
||||
// The task will run on the current (virtual) thread's default task queue.
|
||||
base::SequencedTaskRunnerHandle::Get()->PostTask(
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&Task);
|
||||
```
|
||||
|
||||
Note that `SequencedTaskRunnerHandle::Get()` returns the default queue for the
|
||||
Note that `SequencedTaskRunner::GetCurrentDefault()` returns the default queue for the
|
||||
current virtual thread. On threads with multiple task queues (e.g.
|
||||
BrowserThread::UI) this can be a different queue than the one the current task
|
||||
belongs to. The "current" task runner is intentionally not exposed via a static
|
||||
getter. Either you know it already and can post to it directly or you don't and
|
||||
the only sensible destination is the default queue.
|
||||
the only sensible destination is the default queue. See https://bit.ly/3JvCLsX
|
||||
for detailed discussion.
|
||||
|
||||
## Using Sequences Instead of Locks
|
||||
|
||||
@ -413,21 +414,21 @@ be necessary.
|
||||
*** note
|
||||
**IMPORTANT:** To post a task that needs mutual exclusion with the current
|
||||
sequence of tasks but doesn’t absolutely need to run on the current physical
|
||||
thread, use `base::SequencedTaskRunnerHandle::Get()` instead of
|
||||
`base::ThreadTaskRunnerHandle::Get()` (ref. [Posting to the Current
|
||||
thread, use `base::SequencedTaskRunner::GetCurrentDefault()` instead of
|
||||
`base::SingleThreadTaskRunner::GetCurrentDefault()` (ref. [Posting to the Current
|
||||
Sequence](#Posting-to-the-Current-Virtual_Thread)). That will better document
|
||||
the requirements of the posted task and will avoid unnecessarily making your API
|
||||
physical thread-affine. In a single-thread task,
|
||||
`base::SequencedTaskRunnerHandle::Get()` is equivalent to
|
||||
`base::ThreadTaskRunnerHandle::Get()`.
|
||||
`base::SequencedTaskRunner::GetCurrentDefault()` is equivalent to
|
||||
`base::SingleThreadTaskRunner::GetCurrentDefault()`.
|
||||
***
|
||||
|
||||
If you must post a task to the current physical thread nonetheless, use
|
||||
[`base::ThreadTaskRunnerHandle`](https://cs.chromium.org/chromium/src/base/threading/thread_task_runner_handle.h).
|
||||
[`base::SingleThreadTaskRunner::CurrentDefaultHandle`](https://source.chromium.org/chromium/chromium/src/+/main:base/task/single_thread_task_runner.h).
|
||||
|
||||
```cpp
|
||||
// The task will run on the current thread in the future.
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&Task));
|
||||
```
|
||||
|
||||
@ -450,7 +451,7 @@ void TaskAUsingCOMSTA() {
|
||||
// ...
|
||||
|
||||
// Post another task to the current COM STA thread.
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
|
||||
FROM_HERE, base::BindOnce(&TaskCUsingCOMSTA));
|
||||
}
|
||||
void TaskBUsingCOMSTA() { }
|
||||
@ -700,8 +701,8 @@ after max concurrency increases.
|
||||
For more details see [Testing Components Which Post
|
||||
Tasks](threading_and_tasks_testing.md).
|
||||
|
||||
To test code that uses `base::ThreadTaskRunnerHandle`,
|
||||
`base::SequencedTaskRunnerHandle` or a function in
|
||||
To test code that uses `base::SingleThreadTaskRunner::CurrentDefaultHandle`,
|
||||
`base::SequencedTaskRunner::CurrentDefaultHandle` or a function in
|
||||
[`base/task/thread_pool.h`](https://cs.chromium.org/chromium/src/base/task/thread_pool.h),
|
||||
instantiate a
|
||||
[`base::test::TaskEnvironment`](https://cs.chromium.org/chromium/src/base/test/task_environment.h)
|
||||
@ -728,13 +729,13 @@ class MyTest : public testing::Test {
|
||||
};
|
||||
|
||||
TEST(MyTest, MyTest) {
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&A));
|
||||
base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
|
||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, base::BindOnce(&A));
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
|
||||
base::BindOnce(&B));
|
||||
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
||||
FROM_HERE, base::BindOnce(&C), base::TimeDelta::Max());
|
||||
|
||||
// This runs the (Thread|Sequenced)TaskRunnerHandle queue until it is empty.
|
||||
// This runs the (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle queue until it is empty.
|
||||
// Delayed tasks are not added to the queue until they are ripe for execution.
|
||||
// Prefer explicit exit conditions to RunUntilIdle when possible:
|
||||
// bit.ly/run-until-idle-with-care2.
|
||||
@ -742,11 +743,11 @@ TEST(MyTest, MyTest) {
|
||||
// A and B have been executed. C is not ripe for execution yet.
|
||||
|
||||
base::RunLoop run_loop;
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&D));
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure());
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::BindOnce(&E));
|
||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, base::BindOnce(&D));
|
||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, run_loop.QuitClosure());
|
||||
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, base::BindOnce(&E));
|
||||
|
||||
// This runs the (Thread|Sequenced)TaskRunnerHandle queue until QuitClosure is
|
||||
// This runs the (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle queue until QuitClosure is
|
||||
// invoked.
|
||||
run_loop.Run();
|
||||
// D and run_loop.QuitClosure() have been executed. E is still in the queue.
|
||||
@ -764,8 +765,8 @@ TEST(MyTest, MyTest) {
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE, {}, base::BindOnce(&H), base::BindOnce(&I));
|
||||
|
||||
// This runs the (Thread|Sequenced)TaskRunnerHandle queue until both the
|
||||
// (Thread|Sequenced)TaskRunnerHandle queue and the ThreadPool queue are
|
||||
// This runs the (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle queue until both the
|
||||
// (SingleThread|Sequenced)TaskRunner::CurrentDefaultHandle queue and the ThreadPool queue are
|
||||
// empty. Prefer explicit exit conditions to RunUntilIdle when possible:
|
||||
// bit.ly/run-until-idle-with-care2.
|
||||
task_environment_.RunUntilIdle();
|
||||
@ -920,7 +921,7 @@ following:
|
||||
|
||||
* base::RunLoop: Drive the SequenceManager from the thread it's bound to.
|
||||
|
||||
* base::Thread/SequencedTaskRunnerHandle: Post back to the SequenceManager TaskQueues from a task running on it.
|
||||
* base::Thread/SequencedTaskRunner::CurrentDefaultHandle: Post back to the SequenceManager TaskQueues from a task running on it.
|
||||
|
||||
* SequenceLocalStorageSlot : Bind external state to a sequence.
|
||||
|
||||
|
@ -40,10 +40,10 @@ public Run\*() methods that merely forward to the private member).
|
||||
|
||||
### base::test::SingleThreadTaskEnvironment
|
||||
|
||||
Your component uses `base::ThreadTaskRunnerHandle::Get()` or
|
||||
`base::SequencedTaskRunnerHandle::Get()` to post tasks to the thread it was
|
||||
created on? You'll need at least a `base::test::SingleThreadTaskEnvironment` in
|
||||
order for these APIs to be functional and `base::RunLoop` to run the posted
|
||||
Your component uses `base::SingleThreadTaskRunner::GetCurrentDefault()` or
|
||||
`base::SequencedTaskRunner::GetCurrentDefault()` to post tasks to the thread it
|
||||
was created on? You'll need at least a `base::test::SingleThreadTaskEnvironment`
|
||||
in order for these APIs to be functional and `base::RunLoop` to run the posted
|
||||
tasks.
|
||||
|
||||
Typically this will look something like this:
|
||||
@ -52,7 +52,7 @@ foo.h
|
||||
```c++
|
||||
class Foo {
|
||||
public:
|
||||
Foo() : owning_sequence_(base::SequencedTaskRunnerHandle::Get()) {}
|
||||
Foo() : owning_sequence_(base::SequencedTaskRunner::GetCurrentDefault()) {}
|
||||
|
||||
DoSomethingAndReply(base::OnceClosure on_done) {
|
||||
DCHECK(owning_sequence_->RunsTasksInCurrentSequence());
|
||||
|
@ -22,12 +22,22 @@ import sys
|
||||
|
||||
_DISALLOW_NON_BLINK_MOJOM = (
|
||||
# network::mojom::Foo is allowed to use as non-blink mojom type.
|
||||
'(|::)(?!network::)(\w+::)?mojom::(?!blink).+',
|
||||
'(?!network::)(\w+::)?mojom::(?!blink).+',
|
||||
'Using non-blink mojom types, consider using "::mojom::blink::Foo" instead'
|
||||
'of "::mojom::Foo" unless you have clear reasons not to do so.',
|
||||
'Warning')
|
||||
|
||||
_CONFIG = [
|
||||
{
|
||||
'paths': ['third_party/blink/'],
|
||||
# These task runners are generally banned in blink to ensure
|
||||
# that blink tasks remain properly labeled. See
|
||||
# //third_party/blink/renderer/platform/scheduler/TaskSchedulingInBlink.md
|
||||
# for more.
|
||||
'inclass_disallowed': [
|
||||
'base::(SingleThread|Sequenced)TaskRunner::(GetCurrentDefault|CurrentDefaultHandle)',
|
||||
]
|
||||
},
|
||||
{
|
||||
'paths': ['third_party/blink/renderer/'],
|
||||
'allowed': [
|
||||
@ -1305,6 +1315,9 @@ _CONFIG = [
|
||||
'libopus::.+',
|
||||
'libyuv::.+',
|
||||
'video_track_recorder::.+',
|
||||
],
|
||||
'inclass_allowed': [
|
||||
'base::(SingleThread|Sequenced)TaskRunner::(CurrentDefaultHandle|GetCurrentDefault)'
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -1349,6 +1362,9 @@ _CONFIG = [
|
||||
'webrtc::StreamConfig',
|
||||
'webrtc::TypingDetection',
|
||||
'webrtc::VideoTrackInterface',
|
||||
],
|
||||
'inclass_allowed': [
|
||||
'base::(SingleThread|Sequenced)TaskRunner::(CurrentDefaultHandle|GetCurrentDefault)'
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -1833,6 +1849,8 @@ def _precompile_config():
|
||||
compiled_config = []
|
||||
for raw_entry in _CONFIG:
|
||||
disallowed, advice = compile_disallowed(raw_entry.get('disallowed'))
|
||||
inclass_disallowed, inclass_advice = compile_disallowed(
|
||||
raw_entry.get('inclass_disallowed'))
|
||||
compiled_config.append({
|
||||
'paths':
|
||||
raw_entry['paths'],
|
||||
@ -1842,23 +1860,50 @@ def _precompile_config():
|
||||
disallowed,
|
||||
'advice':
|
||||
advice,
|
||||
'inclass_allowed':
|
||||
compile_regexp(raw_entry.get('inclass_allowed')),
|
||||
'inclass_disallowed':
|
||||
inclass_disallowed,
|
||||
'inclass_advice':
|
||||
inclass_advice,
|
||||
})
|
||||
return compiled_config
|
||||
|
||||
|
||||
_COMPILED_CONFIG = _precompile_config()
|
||||
|
||||
# Attempt to match identifiers qualified with a namespace. Since parsing C++ in
|
||||
# Python is hard, this regex assumes that namespace names only contain lowercase
|
||||
# letters, numbers, and underscores, matching the Google C++ style guide. This
|
||||
# is intended to minimize the number of matches where :: is used to qualify a
|
||||
# name with a class or enum name.
|
||||
# Attempt to match identifiers qualified with a namespace. Since
|
||||
# parsing C++ in Python is hard, this regex assumes that namespace
|
||||
# names only contain lowercase letters, numbers, and underscores,
|
||||
# matching the Google C++ style guide. This is intended to minimize
|
||||
# the number of matches where :: is used to qualify a name with a
|
||||
# class or enum name.
|
||||
#
|
||||
# As a bit of a minor hack, this regex also hardcodes a check for GURL, since
|
||||
# GURL isn't namespace qualified and wouldn't match otherwise.
|
||||
#
|
||||
# An example of an identifier that will be matched with this RE is
|
||||
# "base::BindOnce" or "performance_manager::policies::WorkingSetTrimData".
|
||||
_IDENTIFIER_WITH_NAMESPACE_RE = re.compile(
|
||||
r'\b(?:(?:[a-z_][a-z0-9_]*::)+[A-Za-z_][A-Za-z0-9_]*|GURL)\b')
|
||||
|
||||
# Different check which matches a non-empty sequence of lower-case
|
||||
# alphanumeric namespaces, followed by at least one
|
||||
# upper-or-lower-case alphanumeric qualifier, followed by the
|
||||
# upper-or-lower-case alphanumeric identifier. This matches
|
||||
# identifiers which are within a class.
|
||||
#
|
||||
# In case identifiers are matched by both _IDENTIFIER_IN_CLASS and
|
||||
# _IDENTIFIER_WITH_NAMESPACE, the behavior of
|
||||
# _IDENTIFIER_WITH_NAMESPACE takes precedence, and it is as if it was
|
||||
# not matched by _IDENTIFIER_IN_CLASS.
|
||||
#
|
||||
# An example of an identifier matched by this regex is
|
||||
# "base::SingleThreadTaskRunner::CurrentDefaultHandle".
|
||||
_IDENTIFIER_IN_CLASS_RE = re.compile(
|
||||
r'\b(?:[a-z_][a-z0-9_]*::)+(?:[A-Za-z_][A-Za-z0-9_]*::)+[A-Za-z_][A-Za-z0-9_]*\b'
|
||||
)
|
||||
|
||||
|
||||
def _find_matching_entries(path):
|
||||
"""Finds entries that should be used for path.
|
||||
@ -1881,24 +1926,34 @@ def _find_matching_entries(path):
|
||||
return [entry['entry'] for entry in entries]
|
||||
|
||||
|
||||
def _check_entries_for_identifier(entries, identifier):
|
||||
def _check_entries_for_identifier(entries, identifier, in_class=False):
|
||||
"""Check if an identifier is allowed"""
|
||||
for entry in entries:
|
||||
if entry['disallowed'].match(identifier):
|
||||
return False
|
||||
if entry['allowed'].match(identifier):
|
||||
return True
|
||||
# Disallow by default.
|
||||
return False
|
||||
if in_class:
|
||||
if entry['inclass_disallowed'].match(identifier):
|
||||
return False
|
||||
if entry['inclass_allowed'].match(identifier):
|
||||
return True
|
||||
else:
|
||||
if entry['disallowed'].match(identifier):
|
||||
return False
|
||||
if entry['allowed'].match(identifier):
|
||||
return True
|
||||
# Identifiers in classes which are allowed are allowed by default,
|
||||
# while non-inner identifiers are disallowed by default.
|
||||
return in_class
|
||||
|
||||
|
||||
def _find_advice_for_identifier(entries, identifier):
|
||||
def _find_advice_for_identifier(entries, identifier, in_class=False):
|
||||
advice_list = []
|
||||
all_warning = True
|
||||
for entry in entries:
|
||||
for matcher, advice, warning in entry.get('advice', []):
|
||||
for matcher, advice, warning in entry.get(
|
||||
'inclass_advice' if in_class else 'advice', []):
|
||||
if matcher.match(identifier):
|
||||
advice_list.append(advice)
|
||||
return advice_list, warning
|
||||
all_warning = all_warning and warning
|
||||
return advice_list, all_warning
|
||||
|
||||
|
||||
class BadIdentifier(object):
|
||||
@ -1939,13 +1994,22 @@ def check(path, contents):
|
||||
idx = line.find('//')
|
||||
if idx >= 0:
|
||||
line = line[:idx]
|
||||
identifiers = _IDENTIFIER_WITH_NAMESPACE_RE.findall(line)
|
||||
identifiers = set(_IDENTIFIER_WITH_NAMESPACE_RE.findall(line))
|
||||
in_class_identifiers = set(_IDENTIFIER_IN_CLASS_RE.findall(line))
|
||||
in_class_identifiers -= identifiers
|
||||
for identifier in identifiers:
|
||||
if not _check_entries_for_identifier(entries, identifier):
|
||||
advice, warning = _find_advice_for_identifier(
|
||||
entries, identifier)
|
||||
results.append(
|
||||
BadIdentifier(identifier, line_number, advice, warning))
|
||||
for identifier in in_class_identifiers:
|
||||
if not _check_entries_for_identifier(entries, identifier, True):
|
||||
advice, warning = _find_advice_for_identifier(
|
||||
entries, identifier, True)
|
||||
results.append(
|
||||
BadIdentifier(identifier, line_number, advice, warning))
|
||||
|
||||
return results
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from blinkpy.presubmit import audit_non_blink_usage
|
||||
from blinkpy.presubmit import audit_non_blink_usage as audit
|
||||
|
||||
|
||||
class TestAuditNonBlinkUsageTest(unittest.TestCase):
|
||||
@ -16,57 +16,97 @@ class TestAuditNonBlinkUsageTest(unittest.TestCase):
|
||||
def test_valid_compiled_config(self):
|
||||
# We need to test this protected data.
|
||||
# pylint: disable=W0212
|
||||
for entry in audit_non_blink_usage._COMPILED_CONFIG:
|
||||
for entry in audit._COMPILED_CONFIG:
|
||||
for path in entry['paths']:
|
||||
self.assertIsInstance(path, str)
|
||||
if 'allowed' in entry:
|
||||
self.assertIsInstance(entry['allowed'], self._REGEXP_CLASS)
|
||||
if 'disallowed' in entry:
|
||||
self.assertIsInstance(entry['disallowed'], self._REGEXP_CLASS)
|
||||
if 'inclass_allowed' in entry:
|
||||
self.assertIsInstance(entry['inclass_allowed'],
|
||||
self._REGEXP_CLASS)
|
||||
if 'inclass_disallowed' in entry:
|
||||
self.assertIsInstance(entry['inclass_disallowed'],
|
||||
self._REGEXP_CLASS)
|
||||
for match, advice, warning in entry.get('advice', []):
|
||||
self.assertIsInstance(match, self._REGEXP_CLASS)
|
||||
self.assertIsInstance(advice, str)
|
||||
for match, advice, warning in entry.get('inclass_advice', []):
|
||||
self.assertIsInstance(match, self._REGEXP_CLASS)
|
||||
self.assertIsInstance(advice, str)
|
||||
|
||||
def test_for_special_cases(self):
|
||||
for entry in audit_non_blink_usage._COMPILED_CONFIG:
|
||||
if entry['paths'] == ['third_party/blink/renderer/']:
|
||||
check_list = [
|
||||
{
|
||||
'type': 'url::mojom::Origin',
|
||||
'allowed': False
|
||||
},
|
||||
{
|
||||
'type': '::media::mojom::InterfaceFactory',
|
||||
'allowed': False
|
||||
},
|
||||
{
|
||||
'type': 'Hogenetwork::mojom::URLLoaderFactory',
|
||||
'allowed': False
|
||||
},
|
||||
{
|
||||
'type': 'url::mojom::blink::Origin',
|
||||
'allowed': True
|
||||
},
|
||||
{
|
||||
'type': '::media::mojom::blink::InterfaceFactory',
|
||||
'allowed': True
|
||||
},
|
||||
{
|
||||
'type': 'network::mojom::URLLoaderFactory',
|
||||
'allowed': True
|
||||
},
|
||||
{
|
||||
'type': '::network::mojom::URLLoaderFactory',
|
||||
'allowed': True
|
||||
},
|
||||
]
|
||||
for item in check_list:
|
||||
if item['allowed']:
|
||||
self.assertIsNone(
|
||||
re.match(entry['disallowed'], item['type']))
|
||||
elif not item['allowed']:
|
||||
self.assertIsNotNone(
|
||||
re.match(entry['disallowed'], item['type']))
|
||||
check_list = [
|
||||
{
|
||||
'type': 'url::mojom::Origin',
|
||||
'allowed': False,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'media::mojom::InterfaceFactory',
|
||||
'allowed': False,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'url::mojom::blink::Origin',
|
||||
'allowed': True,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'media::mojom::blink::InterfaceFactory',
|
||||
'allowed': True,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'network::mojom::URLLoaderFactory',
|
||||
'allowed': True,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'base::SingleThreadTaskRunner::GetCurrentDefault',
|
||||
'allowed': False,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'base::SingleThreadTaskRunner::CurrentDefaultHandle',
|
||||
'allowed': False,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'base::SingleThreadTaskRunner',
|
||||
'allowed': True,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'base::SingleThreadTaskRunner::OtherAPI',
|
||||
'allowed': True,
|
||||
'path': 'third_party/blink/renderer/'
|
||||
},
|
||||
{
|
||||
'type': 'base::SingleThreadTaskRunner::GetCurrentDefault',
|
||||
'allowed': True,
|
||||
'path': 'third_party/blink/renderer/modules/mediarecorder/'
|
||||
},
|
||||
{
|
||||
'type': 'base::SingleThreadTaskRunner::CurrentDefaultHandle',
|
||||
'allowed': True,
|
||||
'path': 'third_party/blink/renderer/modules/mediarecorder/'
|
||||
},
|
||||
]
|
||||
for item in check_list:
|
||||
# Make sure that the identifier we're testing is parsed
|
||||
# fully.
|
||||
self.assertTrue(
|
||||
audit._IDENTIFIER_IN_CLASS_RE.fullmatch(item['type']) is
|
||||
not None or audit._IDENTIFIER_WITH_NAMESPACE_RE.fullmatch(
|
||||
item['type']) is not None)
|
||||
# Use the mechanism in the presubmit source code to find
|
||||
# which rules to match.
|
||||
entries = audit._find_matching_entries(item['path'])
|
||||
in_class = audit._IDENTIFIER_WITH_NAMESPACE_RE.fullmatch(
|
||||
item['type']) is None
|
||||
allowed = audit._check_entries_for_identifier(
|
||||
entries, item['type'], in_class)
|
||||
self.assertEqual(allowed, item['allowed'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Reference in New Issue
Block a user