Commit 1ef212bd authored by Gabriel Charette's avatar Gabriel Charette Committed by Commit Bot

Introduce RunLoop::OverrideDelegateForCurrentThreadForTesting().

This is a prerequisite to enable ScopedTaskEnvironment MOCK_TIME
on top of any RunLoop::Delegate (i.e. on top of MessageLoopForUI/ForIO).

This CL also removes RunLoop::Delegate::Client::IsNested() as it
was a mere shortcut for its TLS complement and had to switch to
using TLS itself to remain valid in override scenarios...
Ran base_perftests.exe --gtest_filter=*MessageLoop* in static/Release
and things look the same.

Bug: 708584
Change-Id: I143f6e6afb47de11f95702c337dbe63eb0887596
Reviewed-on: https://chromium-review.googlesource.com/784214
Commit-Queue: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521228}
parent 46c535d2
...@@ -334,7 +334,7 @@ void MessageLoop::Run(bool application_tasks_allowed) { ...@@ -334,7 +334,7 @@ void MessageLoop::Run(bool application_tasks_allowed) {
DCHECK_EQ(this, current()); DCHECK_EQ(this, current());
if (application_tasks_allowed && !task_execution_allowed_) { if (application_tasks_allowed && !task_execution_allowed_) {
// Allow nested task execution as explicitly requested. // Allow nested task execution as explicitly requested.
DCHECK(run_loop_client_->IsNested()); DCHECK(RunLoop::IsNestedOnCurrentThread());
task_execution_allowed_ = true; task_execution_allowed_ = true;
pump_->Run(this); pump_->Run(this);
task_execution_allowed_ = false; task_execution_allowed_ = false;
...@@ -363,7 +363,7 @@ void MessageLoop::SetThreadTaskRunnerHandle() { ...@@ -363,7 +363,7 @@ void MessageLoop::SetThreadTaskRunnerHandle() {
} }
bool MessageLoop::ProcessNextDelayedNonNestableTask() { bool MessageLoop::ProcessNextDelayedNonNestableTask() {
if (run_loop_client_->IsNested()) if (RunLoop::IsNestedOnCurrentThread())
return false; return false;
while (incoming_task_queue_->deferred_tasks().HasTasks()) { while (incoming_task_queue_->deferred_tasks().HasTasks()) {
...@@ -399,7 +399,7 @@ void MessageLoop::RunTask(PendingTask* pending_task) { ...@@ -399,7 +399,7 @@ void MessageLoop::RunTask(PendingTask* pending_task) {
bool MessageLoop::DeferOrRunPendingTask(PendingTask pending_task) { bool MessageLoop::DeferOrRunPendingTask(PendingTask pending_task) {
if (pending_task.nestable == Nestable::kNestable || if (pending_task.nestable == Nestable::kNestable ||
!run_loop_client_->IsNested()) { !RunLoop::IsNestedOnCurrentThread()) {
RunTask(&pending_task); RunTask(&pending_task);
// Show that we ran a task (Note: a new one might arrive as a // Show that we ran a task (Note: a new one might arrive as a
// consequence!). // consequence!).
......
...@@ -50,13 +50,8 @@ RunLoop::Delegate::~Delegate() { ...@@ -50,13 +50,8 @@ RunLoop::Delegate::~Delegate() {
bool RunLoop::Delegate::Client::ShouldQuitWhenIdle() const { bool RunLoop::Delegate::Client::ShouldQuitWhenIdle() const {
DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
DCHECK(outer_->bound_); DCHECK(outer_->bound_);
return outer_->active_run_loops_.top()->quit_when_idle_received_; return outer_->was_overriden_ ||
} outer_->active_run_loops_.top()->quit_when_idle_received_;
bool RunLoop::Delegate::Client::IsNested() const {
DCHECK_CALLED_ON_VALID_THREAD(outer_->bound_thread_checker_);
DCHECK(outer_->bound_);
return outer_->active_run_loops_.size() > 1;
} }
RunLoop::Delegate::Client::Client(Delegate* outer) : outer_(outer) {} RunLoop::Delegate::Client::Client(Delegate* outer) : outer_(outer) {}
...@@ -76,6 +71,27 @@ RunLoop::Delegate::Client* RunLoop::RegisterDelegateForCurrentThread( ...@@ -76,6 +71,27 @@ RunLoop::Delegate::Client* RunLoop::RegisterDelegateForCurrentThread(
return &delegate->client_interface_; return &delegate->client_interface_;
} }
// static
std::pair<RunLoop::Delegate::Client*, RunLoop::Delegate*>
RunLoop::OverrideDelegateForCurrentThreadForTesting(Delegate* delegate) {
// Bind |delegate| to this thread.
DCHECK(!delegate->bound_);
DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
// Overriding cannot be performed while running.
DCHECK(!IsRunningOnCurrentThread());
// Override the current Delegate (there must be one).
Delegate* overriden_delegate = tls_delegate.Get().Get();
DCHECK(overriden_delegate);
DCHECK(overriden_delegate->bound_);
overriden_delegate->was_overriden_ = true;
tls_delegate.Get().Set(delegate);
delegate->bound_ = true;
return std::make_pair(&delegate->client_interface_, overriden_delegate);
}
RunLoop::RunLoop(Type type) RunLoop::RunLoop(Type type)
: delegate_(tls_delegate.Get().Get()), : delegate_(tls_delegate.Get().Get()),
type_(type), type_(type),
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef BASE_RUN_LOOP_H_ #ifndef BASE_RUN_LOOP_H_
#define BASE_RUN_LOOP_H_ #define BASE_RUN_LOOP_H_
#include <utility>
#include <vector> #include <vector>
#include "base/base_export.h" #include "base/base_export.h"
...@@ -159,10 +160,11 @@ class BASE_EXPORT RunLoop { ...@@ -159,10 +160,11 @@ class BASE_EXPORT RunLoop {
class BASE_EXPORT Delegate { class BASE_EXPORT Delegate {
public: public:
Delegate(); Delegate();
~Delegate(); virtual ~Delegate();
// The client interface provided back to the caller who registers this // The client interface provided back to the caller who registers this
// Delegate via RegisterDelegateForCurrentThread. // Delegate via RegisterDelegateForCurrentThread() or
// OverrideDelegateForCurrentThreadForTesting().
class BASE_EXPORT Client { class BASE_EXPORT Client {
public: public:
// Returns true if the Delegate should return from the topmost Run() when // Returns true if the Delegate should return from the topmost Run() when
...@@ -170,11 +172,6 @@ class BASE_EXPORT RunLoop { ...@@ -170,11 +172,6 @@ class BASE_EXPORT RunLoop {
// becomes idle. // becomes idle.
bool ShouldQuitWhenIdle() const; bool ShouldQuitWhenIdle() const;
// Returns true if this |outer_| is currently in nested runs. This is a
// shortcut for RunLoop::IsNestedOnCurrentThread() for the owner of this
// interface.
bool IsNested() const;
private: private:
// Only a Delegate can instantiate a Delegate::Client. // Only a Delegate can instantiate a Delegate::Client.
friend class Delegate; friend class Delegate;
...@@ -221,6 +218,12 @@ class BASE_EXPORT RunLoop { ...@@ -221,6 +218,12 @@ class BASE_EXPORT RunLoop {
RunLoopStack active_run_loops_; RunLoopStack active_run_loops_;
ObserverList<RunLoop::NestingObserver> nesting_observers_; ObserverList<RunLoop::NestingObserver> nesting_observers_;
// True if this Delegate was overriden through
// OverrideDelegateForCurrentThreadForTesting(). It will from then on always
// return from Run() when idle (i.e. its Client's ShouldQuitWhenIdle() will
// always be true).
bool was_overriden_ = false;
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
bool allow_running_for_testing_ = true; bool allow_running_for_testing_ = true;
#endif #endif
...@@ -243,6 +246,15 @@ class BASE_EXPORT RunLoop { ...@@ -243,6 +246,15 @@ class BASE_EXPORT RunLoop {
// Delegate::Client is valid as long as |delegate| is kept alive. // Delegate::Client is valid as long as |delegate| is kept alive.
static Delegate::Client* RegisterDelegateForCurrentThread(Delegate* delegate); static Delegate::Client* RegisterDelegateForCurrentThread(Delegate* delegate);
// Akin to RegisterDelegateForCurrentThread but overrides an existing Delegate
// (there must be one). Returning |delegate|'s client interface like
// RegisterDelegateForCurrentThread() as well as the overriden Delegate which
// the caller is now in charge of driving. The overriden Delegate's Run()
// method will always return when idle (or earlier if the Delegate's Quit()
// method is explicitly invoked).
static std::pair<Delegate::Client*, Delegate*>
OverrideDelegateForCurrentThreadForTesting(Delegate* delegate);
// Quits the active RunLoop (when idle) -- there must be one. These were // Quits the active RunLoop (when idle) -- there must be one. These were
// introduced as prefered temporary replacements to the long deprecated // introduced as prefered temporary replacements to the long deprecated
// MessageLoop::Quit(WhenIdle) methods. Callers should properly plumb a // MessageLoop::Quit(WhenIdle) methods. Callers should properly plumb a
......
...@@ -87,7 +87,7 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner { ...@@ -87,7 +87,7 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
return origin_thread_checker_.CalledOnValidThread(); return origin_thread_checker_.CalledOnValidThread();
} }
bool ProcessTask() { bool ProcessSingleTask() {
OnceClosure task; OnceClosure task;
{ {
AutoLock auto_lock(tasks_lock_); AutoLock auto_lock(tasks_lock_);
...@@ -97,11 +97,18 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner { ...@@ -97,11 +97,18 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
pending_tasks_.pop(); pending_tasks_.pop();
} }
// It's important to Run() after pop() and outside the lock as |task| may // It's important to Run() after pop() and outside the lock as |task| may
// run a nested loop which will re-enter ProcessTask(). // run a nested loop which will re-enter ProcessSingleTask().
std::move(task).Run(); std::move(task).Run();
return true; return true;
} }
base::queue<OnceClosure> TakePendingTasks() {
AutoLock auto_lock(tasks_lock_);
base::queue<OnceClosure> pending_tasks;
std::swap(pending_tasks, pending_tasks_);
return pending_tasks;
}
private: private:
~SimpleSingleThreadTaskRunner() override = default; ~SimpleSingleThreadTaskRunner() override = default;
...@@ -116,49 +123,66 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner { ...@@ -116,49 +123,66 @@ class SimpleSingleThreadTaskRunner : public SingleThreadTaskRunner {
DISALLOW_COPY_AND_ASSIGN(SimpleSingleThreadTaskRunner); DISALLOW_COPY_AND_ASSIGN(SimpleSingleThreadTaskRunner);
}; };
// The basis of all TestDelegates, allows safely injecting a OnceClosure to be
// run in the next idle phase of this delegate's Run() implementation. This can
// be used to have code run on a thread that is otherwise livelocked in an idle
// phase (sometimes a simple PostTask() won't do it -- e.g. when processing
// application tasks is disallowed).
class InjectableTestDelegate : public RunLoop::Delegate {
public:
void InjectClosureOnDelegate(OnceClosure closure) {
AutoLock auto_lock(closure_lock_);
closure_ = std::move(closure);
}
bool RunInjectedClosure() {
AutoLock auto_lock(closure_lock_);
if (closure_.is_null())
return false;
std::move(closure_).Run();
return true;
}
private:
Lock closure_lock_;
OnceClosure closure_;
};
// A simple test RunLoop::Delegate to exercise Runloop logic independent of any // A simple test RunLoop::Delegate to exercise Runloop logic independent of any
// other base constructs. // other base constructs. BindToCurrentThread() must be called before this
class TestDelegate final : public RunLoop::Delegate { // TestBoundDelegate is operational.
class TestBoundDelegate final : public InjectableTestDelegate {
public: public:
TestDelegate() = default; TestBoundDelegate() = default;
// Makes this TestBoundDelegate become the RunLoop::Delegate and
// ThreadTaskRunnerHandle for this thread.
void BindToCurrentThread() { void BindToCurrentThread() {
DCHECK(!run_loop_client_);
thread_task_runner_handle_ = thread_task_runner_handle_ =
std::make_unique<ThreadTaskRunnerHandle>(simple_task_runner_); std::make_unique<ThreadTaskRunnerHandle>(simple_task_runner_);
run_loop_client_ = RunLoop::RegisterDelegateForCurrentThread(this); run_loop_client_ = RunLoop::RegisterDelegateForCurrentThread(this);
} }
// Runs |closure| on the TestDelegate thread as part of Run(). Useful to
// inject code in an otherwise livelocked Run() state.
void RunClosureOnDelegate(OnceClosure closure) {
AutoLock auto_lock(closure_lock_);
closure_ = std::move(closure);
}
private: private:
void Run(bool application_tasks_allowed) override { void Run(bool application_tasks_allowed) override {
if (nested_run_allowing_tasks_incoming_) { if (nested_run_allowing_tasks_incoming_) {
EXPECT_TRUE(run_loop_client_->IsNested()); EXPECT_TRUE(RunLoop::IsNestedOnCurrentThread());
EXPECT_TRUE(application_tasks_allowed); EXPECT_TRUE(application_tasks_allowed);
} else if (run_loop_client_->IsNested()) { } else if (RunLoop::IsNestedOnCurrentThread()) {
EXPECT_FALSE(application_tasks_allowed); EXPECT_FALSE(application_tasks_allowed);
} }
nested_run_allowing_tasks_incoming_ = false; nested_run_allowing_tasks_incoming_ = false;
while (!should_quit_) { while (!should_quit_) {
if (application_tasks_allowed && simple_task_runner_->ProcessTask()) if (application_tasks_allowed && simple_task_runner_->ProcessSingleTask())
continue; continue;
if (run_loop_client_->ShouldQuitWhenIdle()) if (run_loop_client_->ShouldQuitWhenIdle())
break; break;
{ if (RunInjectedClosure())
AutoLock auto_lock(closure_lock_); continue;
if (!closure_.is_null()) {
std::move(closure_).Run();
continue;
}
}
PlatformThread::YieldCurrentThread(); PlatformThread::YieldCurrentThread();
} }
...@@ -177,12 +201,83 @@ class TestDelegate final : public RunLoop::Delegate { ...@@ -177,12 +201,83 @@ class TestDelegate final : public RunLoop::Delegate {
scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_ = scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_ =
MakeRefCounted<SimpleSingleThreadTaskRunner>(); MakeRefCounted<SimpleSingleThreadTaskRunner>();
std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_; std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle_;
bool should_quit_ = false; bool should_quit_ = false;
Lock closure_lock_; RunLoop::Delegate::Client* run_loop_client_ = nullptr;
OnceClosure closure_; };
// A test RunLoop::Delegate meant to override an existing RunLoop::Delegate.
// TakeOverCurrentThread() must be called before this TestBoundDelegate is
// operational.
class TestOverridingDelegate final : public InjectableTestDelegate {
public:
TestOverridingDelegate() = default;
// Overrides the existing RunLoop::Delegate and ThreadTaskRunnerHandles on
// this thread with this TestOverridingDelegate's.
void TakeOverCurrentThread() {
DCHECK(!run_loop_client_);
overriden_task_runner_ = ThreadTaskRunnerHandle::Get();
DCHECK(overriden_task_runner_);
thread_task_runner_handle_override_scope_ =
ThreadTaskRunnerHandle::OverrideForTesting(
simple_task_runner_,
ThreadTaskRunnerHandle::OverrideType::kTakeOverThread);
auto delegate_override_pair =
RunLoop::OverrideDelegateForCurrentThreadForTesting(this);
run_loop_client_ = delegate_override_pair.first;
overriden_delegate_ = delegate_override_pair.second;
DCHECK(overriden_delegate_);
}
private:
void Run(bool application_tasks_allowed) override {
while (!should_quit_) {
auto pending_tasks = simple_task_runner_->TakePendingTasks();
if (!pending_tasks.empty()) {
while (!pending_tasks.empty()) {
overriden_task_runner_->PostTask(FROM_HERE,
std::move(pending_tasks.front()));
pending_tasks.pop();
}
overriden_delegate_->Run(application_tasks_allowed);
continue;
}
if (run_loop_client_->ShouldQuitWhenIdle())
break;
if (RunInjectedClosure())
continue;
PlatformThread::YieldCurrentThread();
}
should_quit_ = false;
}
void Quit() override {
should_quit_ = true;
overriden_delegate_->Quit();
}
void EnsureWorkScheduled() override {
overriden_delegate_->EnsureWorkScheduled();
}
scoped_refptr<SimpleSingleThreadTaskRunner> simple_task_runner_ =
MakeRefCounted<SimpleSingleThreadTaskRunner>();
ScopedClosureRunner thread_task_runner_handle_override_scope_;
scoped_refptr<SingleThreadTaskRunner> overriden_task_runner_;
RunLoop::Delegate* overriden_delegate_;
bool should_quit_ = false;
RunLoop::Delegate::Client* run_loop_client_ = nullptr; RunLoop::Delegate::Client* run_loop_client_ = nullptr;
}; };
...@@ -195,6 +290,10 @@ enum class RunLoopTestType { ...@@ -195,6 +290,10 @@ enum class RunLoopTestType {
// Runs all RunLoopTests under a test RunLoop::Delegate to make sure the // Runs all RunLoopTests under a test RunLoop::Delegate to make sure the
// delegate interface fully works standalone. // delegate interface fully works standalone.
kTestDelegate, kTestDelegate,
// Runs all RunLoopTests through a RunLoop::Delegate which overrides a
// kRealEnvironment's registered RunLoop::Delegate.
kOverridingTestDelegate,
}; };
// The task environment for the RunLoopTest of a given type. A separate class // The task environment for the RunLoopTest of a given type. A separate class
...@@ -203,20 +302,31 @@ class RunLoopTestEnvironment { ...@@ -203,20 +302,31 @@ class RunLoopTestEnvironment {
public: public:
RunLoopTestEnvironment(RunLoopTestType type) { RunLoopTestEnvironment(RunLoopTestType type) {
switch (type) { switch (type) {
case RunLoopTestType::kRealEnvironment: case RunLoopTestType::kRealEnvironment: {
task_environment_ = std::make_unique<test::ScopedTaskEnvironment>(); task_environment_ = std::make_unique<test::ScopedTaskEnvironment>();
break; break;
case RunLoopTestType::kTestDelegate: }
test_delegate_ = std::make_unique<TestDelegate>(); case RunLoopTestType::kTestDelegate: {
test_delegate_->BindToCurrentThread(); auto test_delegate = std::make_unique<TestBoundDelegate>();
test_delegate->BindToCurrentThread();
test_delegate_ = std::move(test_delegate);
break;
}
case RunLoopTestType::kOverridingTestDelegate: {
task_environment_ = std::make_unique<test::ScopedTaskEnvironment>();
auto test_delegate = std::make_unique<TestOverridingDelegate>();
test_delegate->TakeOverCurrentThread();
test_delegate_ = std::move(test_delegate);
break; break;
}
} }
} }
private: private:
// Instantiates one or the other based on the RunLoopTestType. // Instantiates one or the other based on the RunLoopTestType (or both in the
// kOverridingTestDelegate case).
std::unique_ptr<test::ScopedTaskEnvironment> task_environment_; std::unique_ptr<test::ScopedTaskEnvironment> task_environment_;
std::unique_ptr<TestDelegate> test_delegate_; std::unique_ptr<InjectableTestDelegate> test_delegate_;
}; };
class RunLoopTest : public testing::TestWithParam<RunLoopTestType> { class RunLoopTest : public testing::TestWithParam<RunLoopTestType> {
...@@ -539,15 +649,19 @@ INSTANTIATE_TEST_CASE_P(Real, ...@@ -539,15 +649,19 @@ INSTANTIATE_TEST_CASE_P(Real,
INSTANTIATE_TEST_CASE_P(Mock, INSTANTIATE_TEST_CASE_P(Mock,
RunLoopTest, RunLoopTest,
testing::Values(RunLoopTestType::kTestDelegate)); testing::Values(RunLoopTestType::kTestDelegate));
INSTANTIATE_TEST_CASE_P(
OverridingMock,
RunLoopTest,
testing::Values(RunLoopTestType::kOverridingTestDelegate));
TEST(RunLoopDeathTest, MustRegisterBeforeInstantiating) { TEST(RunLoopDeathTest, MustRegisterBeforeInstantiating) {
TestDelegate unbound_test_delegate_; TestBoundDelegate unbound_test_delegate_;
// Exercise the DCHECK in RunLoop::RunLoop(). // Exercise the DCHECK in RunLoop::RunLoop().
EXPECT_DCHECK_DEATH({ RunLoop(); }); EXPECT_DCHECK_DEATH({ RunLoop(); });
} }
TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) { TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) {
TestDelegate test_delegate; TestBoundDelegate test_delegate;
test_delegate.BindToCurrentThread(); test_delegate.BindToCurrentThread();
base::Thread other_thread("test"); base::Thread other_thread("test");
...@@ -599,8 +713,8 @@ TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) { ...@@ -599,8 +713,8 @@ TEST(RunLoopDelegateTest, NestableTasksDontRunInDefaultNestedLoops) {
other_thread.task_runner()->PostDelayedTask( other_thread.task_runner()->PostDelayedTask(
FROM_HERE, FROM_HERE,
BindOnce( BindOnce(
[](TestDelegate* test_delegate, OnceClosure injected_closure) { [](TestBoundDelegate* test_delegate, OnceClosure injected_closure) {
test_delegate->RunClosureOnDelegate(std::move(injected_closure)); test_delegate->InjectClosureOnDelegate(std::move(injected_closure));
}, },
Unretained(&test_delegate), nested_run_loop.QuitWhenIdleClosure()), Unretained(&test_delegate), nested_run_loop.QuitWhenIdleClosure()),
TestTimeouts::tiny_timeout()); TestTimeouts::tiny_timeout());
......
...@@ -39,7 +39,8 @@ bool ThreadTaskRunnerHandle::IsSet() { ...@@ -39,7 +39,8 @@ bool ThreadTaskRunnerHandle::IsSet() {
// static // static
ScopedClosureRunner ThreadTaskRunnerHandle::OverrideForTesting( ScopedClosureRunner ThreadTaskRunnerHandle::OverrideForTesting(
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner) { scoped_refptr<SingleThreadTaskRunner> overriding_task_runner,
ThreadTaskRunnerHandle::OverrideType type) {
// OverrideForTesting() is not compatible with a SequencedTaskRunnerHandle // OverrideForTesting() is not compatible with a SequencedTaskRunnerHandle
// being set (but SequencedTaskRunnerHandle::IsSet() includes // being set (but SequencedTaskRunnerHandle::IsSet() includes
// ThreadTaskRunnerHandle::IsSet() so that's discounted as the only valid // ThreadTaskRunnerHandle::IsSet() so that's discounted as the only valid
...@@ -67,7 +68,9 @@ ScopedClosureRunner ThreadTaskRunnerHandle::OverrideForTesting( ...@@ -67,7 +68,9 @@ ScopedClosureRunner ThreadTaskRunnerHandle::OverrideForTesting(
ttrh->task_runner_.swap(overriding_task_runner); ttrh->task_runner_.swap(overriding_task_runner);
auto no_running_during_override = auto no_running_during_override =
std::make_unique<RunLoop::ScopedDisallowRunningForTesting>(); type == OverrideType::kTakeOverThread
? nullptr
: std::make_unique<RunLoop::ScopedDisallowRunningForTesting>();
return ScopedClosureRunner(base::Bind( return ScopedClosureRunner(base::Bind(
[](scoped_refptr<SingleThreadTaskRunner> task_runner_to_restore, [](scoped_refptr<SingleThreadTaskRunner> task_runner_to_restore,
......
...@@ -34,9 +34,19 @@ class BASE_EXPORT ThreadTaskRunnerHandle { ...@@ -34,9 +34,19 @@ class BASE_EXPORT ThreadTaskRunnerHandle {
// ScopedClosureRunners expire in LIFO (stack) order. Note: nesting // ScopedClosureRunners expire in LIFO (stack) order. Note: nesting
// ThreadTaskRunnerHandles isn't generally desired but it's useful in unit // ThreadTaskRunnerHandles isn't generally desired but it's useful in unit
// tests where multiple task runners can share the main thread for simplicity // tests where multiple task runners can share the main thread for simplicity
// and determinism. // and determinism (in which case RunLoop::Run() is banned for the scope of
// the override as it would execute tasks from the wrong task runner). It's
// also useful in unit test frameworks in which a task runner takes over the
// main thread; in that case it's fine to allow running through
// |type = kTakeOverThread| iff RunLoop::Run() will result in running tasks
// posted to the overriding ThreadTaskRunnerHandle.
enum class OverrideType {
kDefault,
kTakeOverThread,
};
static ScopedClosureRunner OverrideForTesting( static ScopedClosureRunner OverrideForTesting(
scoped_refptr<SingleThreadTaskRunner> overriding_task_runner); scoped_refptr<SingleThreadTaskRunner> overriding_task_runner,
OverrideType type = OverrideType::kDefault);
// Binds |task_runner| to the current thread. |task_runner| must belong // Binds |task_runner| to the current thread. |task_runner| must belong
// to the current thread for this to succeed. // to the current thread for this to succeed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment