Skip to content

Commit 0f2d16a

Browse files
Add (Sequenced|SingleThread)TaskRunner::GetCurrentBestEffort
Adds accessors to SequencedTaskRunner and SingleThreadTaskRunner to return a best-effort task runner for the current sequence/thread. This can be used as a convenience from code in layers like components/ that can't refer to explicit BrowserThreads. Currently this only returns a best-effort task runner if called from a task scheduled by SequenceManager, such as a BrowserThread. If not it falls back to calling GetCurrentDefault(). Also adds HasCurrentBestEffort() methods that can be used to check whether GetCurrentBestEffort() will return the current default. R=gab Bug: 441949788 Change-Id: Ie165e61fcf4eddc1ddebcb810558a165d7a28d50 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7121638 Reviewed-by: Stefan Zager <[email protected]> Auto-Submit: Joe Mason <[email protected]> Commit-Queue: Joe Mason <[email protected]> Reviewed-by: Kent Tamura <[email protected]> Reviewed-by: Gabriel Charette <[email protected]> Cr-Commit-Position: refs/heads/main@{#1550107} NOKEYCHECK=True GitOrigin-RevId: 223b0999980933f46fa5e43f16e2056b3fbce0b3
1 parent 07e8446 commit 0f2d16a

8 files changed

+520
-2
lines changed

task/sequence_manager/sequence_manager_impl.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,25 @@ WeakPtr<SequenceManagerImpl> SequenceManagerImpl::GetWeakPtr() {
10841084
return weak_factory_.GetWeakPtr();
10851085
}
10861086

1087+
// static
1088+
scoped_refptr<SingleThreadTaskRunner>
1089+
SequenceManagerImpl::GetCurrentBestEffortTaskRunner(
1090+
PassKey<SingleThreadTaskRunner>) {
1091+
if (SequenceManagerImpl* current = GetCurrent()) {
1092+
if (std::optional<TaskQueue::QueuePriority> best_effort_priority =
1093+
current->GetBestEffortPriority()) {
1094+
// Return the first queue with the right priority.
1095+
for (internal::TaskQueueImpl* task_queue :
1096+
current->main_thread_only().active_queues) {
1097+
if (task_queue->GetQueuePriority() == *best_effort_priority) {
1098+
return task_queue->task_runner();
1099+
}
1100+
}
1101+
}
1102+
}
1103+
return nullptr;
1104+
}
1105+
10871106
void SequenceManagerImpl::SetDefaultTaskRunner(
10881107
scoped_refptr<SingleThreadTaskRunner> task_runner) {
10891108
controller_->SetDefaultTaskRunner(task_runner);

task/sequence_manager/sequence_manager_impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ class BASE_EXPORT SequenceManagerImpl
202202
// How frequently to perform housekeeping tasks (sweeping canceled tasks etc).
203203
static constexpr TimeDelta kReclaimMemoryInterval = Seconds(30);
204204

205+
// Allows SingleThreadTaskRunner to find the best-effort task queue for the
206+
// current thread. Returns nullptr if there isn't one.
207+
static scoped_refptr<SingleThreadTaskRunner> GetCurrentBestEffortTaskRunner(
208+
PassKey<SingleThreadTaskRunner>);
209+
205210
protected:
206211
static std::unique_ptr<ThreadControllerImpl>
207212
CreateThreadControllerImplForCurrentThread(const TickClock* clock);

task/sequenced_task_runner.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "base/functional/bind.h"
1010
#include "base/task/default_delayed_task_handle_delegate.h"
11+
#include "base/task/single_thread_task_runner.h"
1112
#include "base/task/thread_pool/thread_pool_instance.h"
1213
#include "base/time/time.h"
1314

@@ -98,6 +99,23 @@ bool SequencedTaskRunner::HasCurrentDefault() {
9899
return !!current_default_handle && !!current_default_handle->task_runner_;
99100
}
100101

102+
// static
103+
scoped_refptr<SequencedTaskRunner> SequencedTaskRunner::GetCurrentBestEffort() {
104+
// Currently only threads that multiplex several task queues have current
105+
// best-effort task runners.
106+
if (SingleThreadTaskRunner::HasCurrentBestEffort()) {
107+
return SingleThreadTaskRunner::GetCurrentBestEffort();
108+
}
109+
return GetCurrentDefault();
110+
}
111+
112+
// static
113+
bool SequencedTaskRunner::HasCurrentBestEffort() {
114+
// Currently only threads that multiplex several task queues have current
115+
// best-effort task runners.
116+
return SingleThreadTaskRunner::HasCurrentBestEffort();
117+
}
118+
101119
SequencedTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle(
102120
scoped_refptr<SequencedTaskRunner> task_runner)
103121
: CurrentDefaultHandle(std::move(task_runner), MayAlreadyExist{}) {

task/sequenced_task_runner.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,22 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
343343
// (which includes any thread that runs a MessagePump).
344344
[[nodiscard]] static bool HasCurrentDefault();
345345

346+
// Returns a SequencedTaskRunner for the current task. If possible, the
347+
// task runner will schedule tasks with BEST_EFFORT TaskPriority. If not, it
348+
// returns the same value as GetCurrentDefault(). See the comments on
349+
// HasCurrentBestEffort() for more details.
350+
[[nodiscard]] static scoped_refptr<SequencedTaskRunner>
351+
GetCurrentBestEffort();
352+
353+
// Returns true if the current task is running on a sequence that multiplexes
354+
// multiple task queues (eg. BrowserThread::UI). If so, GetCurrentBestEffort()
355+
// will return the task runner for the lowest-priority task queue. Otherwise
356+
// it will call GetCurrentDefault(). So if this and GetCurrentDefault() both
357+
// return false, it's not safe to call GetCurrentBestEffort().
358+
// TODO(crbug.com/441949788): It would also be possible to return true on a
359+
// non-multiplexing sequence that only runs BEST_EFFORT tasks. Implement this.
360+
[[nodiscard]] static bool HasCurrentBestEffort();
361+
346362
class BASE_EXPORT CurrentDefaultHandle {
347363
public:
348364
// Sets the value returned by `SequencedTaskRunner::GetCurrentDefault()` to
@@ -366,7 +382,7 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
366382
// SingleThreadTaskRunner::CurrentHandleOverrideForTesting in unit tests to
367383
// avoid the friend requirement.
368384
friend class SingleThreadTaskRunner;
369-
FRIEND_TEST_ALL_PREFIXES(SequencedTaskRunnerCurrentDefaultHandleTest,
385+
FRIEND_TEST_ALL_PREFIXES(SequencedTaskRunnerCurrentDefaultHandleDeathTest,
370386
OverrideWithNull);
371387
FRIEND_TEST_ALL_PREFIXES(SequencedTaskRunnerCurrentDefaultHandleTest,
372388
OverrideWithNonNull);

task/sequenced_task_runner_unittest.cc

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
#include "base/task/sequenced_task_runner.h"
66

7+
#include <array>
78
#include <utility>
89

10+
#include "base/barrier_closure.h"
911
#include "base/functional/bind.h"
1012
#include "base/functional/callback.h"
1113
#include "base/functional/callback_helpers.h"
@@ -16,6 +18,8 @@
1618
#include "base/run_loop.h"
1719
#include "base/sequence_checker_impl.h"
1820
#include "base/task/sequenced_task_runner.h"
21+
#include "base/task/single_thread_task_runner.h"
22+
#include "base/task/task_traits.h"
1923
#include "base/task/thread_pool.h"
2024
#include "base/test/bind.h"
2125
#include "base/test/gtest_util.h"
@@ -215,6 +219,9 @@ class SequencedTaskRunnerCurrentDefaultHandleTest : public ::testing::Test {
215219
test::TaskEnvironment task_environment_;
216220
};
217221

222+
using SequencedTaskRunnerCurrentDefaultHandleDeathTest =
223+
SequencedTaskRunnerCurrentDefaultHandleTest;
224+
218225
} // namespace
219226

220227
TEST_F(SequencedTaskRunnerCurrentDefaultHandleTest, FromTaskEnvironment) {
@@ -239,7 +246,7 @@ TEST_F(SequencedTaskRunnerCurrentDefaultHandleTest,
239246

240247
// Verify that `CurrentDefaultHandle` can be used to set the current default
241248
// `SequencedTaskRunner` to null in a scope that already has a default.
242-
TEST_F(SequencedTaskRunnerCurrentDefaultHandleTest, OverrideWithNull) {
249+
TEST_F(SequencedTaskRunnerCurrentDefaultHandleDeathTest, OverrideWithNull) {
243250
EXPECT_TRUE(SequencedTaskRunner::HasCurrentDefault());
244251
auto tr1 = SequencedTaskRunner::GetCurrentDefault();
245252
EXPECT_TRUE(tr1);
@@ -292,4 +299,167 @@ TEST(SequencedTaskRunnerCurrentDefaultHandleTestWithoutTaskEnvironment,
292299
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
293300
}
294301

302+
TEST(SequencedTaskRunnerCurrentBestEffortTest, SequenceManagerSingleQueue) {
303+
// TaskEnvironment wraps a SequenceManager with a single default task queue.
304+
test::TaskEnvironment task_env;
305+
EXPECT_FALSE(SequencedTaskRunner::HasCurrentBestEffort());
306+
ASSERT_TRUE(SequencedTaskRunner::HasCurrentDefault());
307+
308+
// Should fall back to returning the default task runner when no best-effort
309+
// task runner is set.
310+
EXPECT_EQ(SequencedTaskRunner::GetCurrentBestEffort(),
311+
SequencedTaskRunner::GetCurrentDefault());
312+
}
313+
314+
TEST(SequencedTaskRunnerCurrentBestEffortTest, SequenceManagerManyQueues) {
315+
// TaskEnvironmentWithMainThreadPriorities wraps a SequenceManager with
316+
// several task queues.
317+
test::TaskEnvironmentWithMainThreadPriorities task_env;
318+
EXPECT_TRUE(SequencedTaskRunner::HasCurrentBestEffort());
319+
ASSERT_TRUE(SequencedTaskRunner::HasCurrentDefault());
320+
ASSERT_TRUE(SequencedTaskRunner::GetCurrentBestEffort());
321+
322+
// The best-effort task runner should NOT be the default.
323+
EXPECT_NE(SequencedTaskRunner::GetCurrentBestEffort(),
324+
SequencedTaskRunner::GetCurrentDefault());
325+
326+
// All should run tasks on the same sequence. They differ only in priority.
327+
328+
// Use SequenceCheckerImpl to make sure it's not a no-op in Release builds.
329+
SequenceCheckerImpl sequence_checker;
330+
331+
RunLoop run_loop;
332+
auto quit_closure = BarrierClosure(2, run_loop.QuitClosure());
333+
SequencedTaskRunner::GetCurrentBestEffort()->PostTask(
334+
FROM_HERE, BindLambdaForTesting([&] {
335+
EXPECT_TRUE(sequence_checker.CalledOnValidSequence())
336+
<< "GetCurrentBestEffort";
337+
}).Then(quit_closure));
338+
SequencedTaskRunner::GetCurrentDefault()->PostTask(
339+
FROM_HERE, BindLambdaForTesting([&] {
340+
EXPECT_TRUE(sequence_checker.CalledOnValidSequence())
341+
<< "GetCurrentDefault";
342+
}).Then(quit_closure));
343+
run_loop.Run();
344+
}
345+
346+
TEST(SequencedTaskRunnerCurrentBestEffortTest, ThreadPoolSingleThreadTask) {
347+
test::TaskEnvironmentWithMainThreadPriorities task_env;
348+
349+
EXPECT_TRUE(SequencedTaskRunner::HasCurrentBestEffort());
350+
auto task_env_best_effort_task_runner =
351+
SequencedTaskRunner::GetCurrentBestEffort();
352+
353+
// Use SequenceCheckerImpl to make sure it's not a no-op in Release builds.
354+
SequenceCheckerImpl sequence_checker;
355+
356+
// Test GetCurrentBestEffort() from a default-priority task and a BEST_EFFORT
357+
// task.
358+
constexpr auto kPriorities =
359+
std::to_array({TaskPriority::USER_BLOCKING, TaskPriority::BEST_EFFORT});
360+
361+
for (auto priority : kPriorities) {
362+
ThreadPool::CreateSingleThreadTaskRunner({priority})
363+
->PostTask(FROM_HERE,
364+
BindLambdaForTesting([&] {
365+
SCOPED_TRACE(priority);
366+
367+
// TODO(crbug.com/441949788): Even if this is on a
368+
// BEST_EFFORT task runner, HasCurrentBestEffort() returns
369+
// false because it only supports SequenceManager task
370+
// queues.
371+
EXPECT_FALSE(SequencedTaskRunner::HasCurrentBestEffort());
372+
ASSERT_TRUE(SequencedTaskRunner::HasCurrentDefault());
373+
374+
// Should fall back to returning the default task runner
375+
// when no best-effort task runner is set.
376+
EXPECT_EQ(SequencedTaskRunner::GetCurrentBestEffort(),
377+
SequencedTaskRunner::GetCurrentDefault());
378+
379+
// It should NOT be the TaskEnvironment's best-effort task
380+
// runner.
381+
EXPECT_NE(SequencedTaskRunner::GetCurrentBestEffort(),
382+
task_env_best_effort_task_runner);
383+
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
384+
}).Then(task_env.QuitClosure()));
385+
task_env.RunUntilQuit();
386+
}
387+
}
388+
389+
TEST(SequencedTaskRunnerCurrentBestEffortTest, ThreadPoolSequencedTask) {
390+
test::TaskEnvironmentWithMainThreadPriorities task_env;
391+
392+
EXPECT_TRUE(SequencedTaskRunner::HasCurrentBestEffort());
393+
auto task_env_best_effort_task_runner =
394+
SequencedTaskRunner::GetCurrentBestEffort();
395+
396+
// Use SequenceCheckerImpl to make sure it's not a no-op in Release builds.
397+
SequenceCheckerImpl sequence_checker;
398+
399+
// Test GetCurrentBestEffort() from a default-priority task and a BEST_EFFORT
400+
// task.
401+
constexpr auto kPriorities =
402+
std::to_array({TaskPriority::USER_BLOCKING, TaskPriority::BEST_EFFORT});
403+
404+
for (auto priority : kPriorities) {
405+
ThreadPool::CreateSequencedTaskRunner({priority})
406+
->PostTask(FROM_HERE,
407+
BindLambdaForTesting([&] {
408+
SCOPED_TRACE(priority);
409+
410+
// TODO(crbug.com/441949788): Even if this is on a
411+
// BEST_EFFORT task runner, HasCurrentBestEffort() returns
412+
// false because it only supports SequenceManager task
413+
// queues.
414+
EXPECT_FALSE(SequencedTaskRunner::HasCurrentBestEffort());
415+
ASSERT_TRUE(SequencedTaskRunner::HasCurrentDefault());
416+
417+
// Should fall back to returning the default task runner
418+
// when no best-effort task runner is set.
419+
EXPECT_EQ(SequencedTaskRunner::GetCurrentBestEffort(),
420+
SequencedTaskRunner::GetCurrentDefault());
421+
422+
// It should NOT be the TaskEnvironment's best-effort task
423+
// runner.
424+
EXPECT_NE(SequencedTaskRunner::GetCurrentBestEffort(),
425+
task_env_best_effort_task_runner);
426+
EXPECT_FALSE(sequence_checker.CalledOnValidSequence());
427+
}).Then(task_env.QuitClosure()));
428+
task_env.RunUntilQuit();
429+
}
430+
}
431+
432+
TEST(SequencedTaskRunnerCurrentBestEffortTest, ThreadPoolUnsequencedTask) {
433+
test::TaskEnvironmentWithMainThreadPriorities task_env;
434+
435+
EXPECT_TRUE(SequencedTaskRunner::HasCurrentBestEffort());
436+
437+
// Test GetCurrentBestEffort() from a default-priority task and a BEST_EFFORT
438+
// task.
439+
constexpr auto kPriorities =
440+
std::to_array({TaskPriority::USER_BLOCKING, TaskPriority::BEST_EFFORT});
441+
442+
for (auto priority : kPriorities) {
443+
ThreadPool::PostTask(
444+
FROM_HERE, {priority},
445+
BindLambdaForTesting([&] {
446+
SCOPED_TRACE(priority);
447+
448+
// The current task isn't bound to a sequence.
449+
EXPECT_FALSE(SequencedTaskRunner::HasCurrentBestEffort());
450+
EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault());
451+
}).Then(task_env.QuitClosure()));
452+
task_env.RunUntilQuit();
453+
}
454+
}
455+
456+
TEST(SequencedTaskRunnerCurrentBestEffortDeathTest, NoContext) {
457+
EXPECT_FALSE(SequencedTaskRunner::HasCurrentBestEffort());
458+
EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault());
459+
// Ensure that GetCurrentBestEffort() doesn't return a value when
460+
// HasCurrentDefault() is false.
461+
EXPECT_CHECK_DEATH(
462+
{ auto task_runner = SequencedTaskRunner::GetCurrentBestEffort(); });
463+
}
464+
295465
} // namespace base

task/single_thread_task_runner.cc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@
1414
#include "base/functional/bind.h"
1515
#include "base/lazy_instance.h"
1616
#include "base/run_loop.h"
17+
#include "base/task/sequence_manager/sequence_manager_impl.h"
18+
#include "base/types/pass_key.h"
1719

1820
namespace base {
1921

2022
namespace {
2123

24+
using SequenceManagerImpl = sequence_manager::internal::SequenceManagerImpl;
25+
2226
constinit thread_local SingleThreadTaskRunner::CurrentDefaultHandle*
2327
current_default_handle = nullptr;
2428

@@ -68,6 +72,22 @@ bool SingleThreadTaskRunner::HasCurrentDefault() {
6872
!!GetCurrentDefaultHandle()->task_runner_;
6973
}
7074

75+
// static
76+
scoped_refptr<SingleThreadTaskRunner>
77+
SingleThreadTaskRunner::GetCurrentBestEffort() {
78+
if (auto task_runner = SequenceManagerImpl::GetCurrentBestEffortTaskRunner(
79+
PassKey<SingleThreadTaskRunner>())) {
80+
return task_runner;
81+
}
82+
return GetCurrentDefault();
83+
}
84+
85+
// static
86+
bool SingleThreadTaskRunner::HasCurrentBestEffort() {
87+
return !!SequenceManagerImpl::GetCurrentBestEffortTaskRunner(
88+
PassKey<SingleThreadTaskRunner>());
89+
}
90+
7191
// static
7292
const scoped_refptr<SingleThreadTaskRunner>&
7393
SingleThreadTaskRunner::GetMainThreadDefault() {
@@ -86,6 +106,21 @@ bool SingleThreadTaskRunner::HasMainThreadDefault() {
86106
!!main_thread_default_handle->task_runner_;
87107
}
88108

109+
// static
110+
scoped_refptr<SingleThreadTaskRunner>
111+
SingleThreadTaskRunner::GetMainThreadBestEffort() {
112+
if (HasMainThreadBestEffort()) {
113+
return main_thread_default_handle->best_effort_task_runner_;
114+
}
115+
return GetMainThreadDefault();
116+
}
117+
118+
// static
119+
bool SingleThreadTaskRunner::HasMainThreadBestEffort() {
120+
return !!main_thread_default_handle &&
121+
!!main_thread_default_handle->best_effort_task_runner_;
122+
}
123+
89124
SingleThreadTaskRunner::CurrentDefaultHandle::CurrentDefaultHandle(
90125
scoped_refptr<SingleThreadTaskRunner> task_runner)
91126
: CurrentDefaultHandle(std::move(task_runner), MayAlreadyExist{}) {
@@ -125,6 +160,11 @@ SingleThreadTaskRunner::CurrentHandleOverrideForTesting::
125160
SingleThreadTaskRunner::MainThreadDefaultHandle::MainThreadDefaultHandle(
126161
scoped_refptr<SingleThreadTaskRunner> task_runner)
127162
: task_runner_(std::move(task_runner)),
163+
// `task_runner` belongs to this thread, so if there's a BEST_EFFORT task
164+
// runner for the thread GetCurrentBestEffortTaskRunner will return it.
165+
best_effort_task_runner_(
166+
SequenceManagerImpl::GetCurrentBestEffortTaskRunner(
167+
PassKey<SingleThreadTaskRunner>())),
128168
previous_handle_(main_thread_default_handle) {
129169
CHECK(!main_thread_default_handle || can_override);
130170
main_thread_default_handle = this;

0 commit comments

Comments
 (0)