Commit 80bdddf5 authored by Francois Doray's avatar Francois Doray Committed by Commit Bot

Add base::RunLoop::NestingObserver::OnExitNestedRunLoop().

This method is called when a nested loop is done running work.
It will help simplify the TaskQueueManager code.

Use case:
The blink scheduler needs to adjust the time domain
when there is a transition between a nested/non-nested scope.
https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/scheduler/renderer/renderer_scheduler_impl.cc?l=2302&rcl=614a67e2c14cf8bb9a06f4fe8da7625cbf1ea7d7
Currently, it checks base::RunLoop::IsNestedOnCurrentThread()
every time a task completes to detect transitions between
a nested/non-nested scope. Code would be simpler with
an explicit notification.

Bug: 783309
Change-Id: I9748c287ad8418831598d84f45a518e4138c5e1b
Reviewed-on: https://chromium-review.googlesource.com/766388Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Commit-Queue: François Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#527112}
parent c83ee95a
...@@ -321,6 +321,11 @@ void RunLoop::AfterRun() { ...@@ -321,6 +321,11 @@ void RunLoop::AfterRun() {
RunLoop* previous_run_loop = RunLoop* previous_run_loop =
active_run_loops_.empty() ? nullptr : active_run_loops_.top(); active_run_loops_.empty() ? nullptr : active_run_loops_.top();
if (previous_run_loop) {
for (auto& observer : delegate_->nesting_observers_)
observer.OnExitNestedRunLoop();
}
// Execute deferred Quit, if any: // Execute deferred Quit, if any:
if (previous_run_loop && previous_run_loop->quit_called_) if (previous_run_loop && previous_run_loop->quit_called_)
delegate_->Quit(); delegate_->Quit();
......
...@@ -130,12 +130,13 @@ class BASE_EXPORT RunLoop { ...@@ -130,12 +130,13 @@ class BASE_EXPORT RunLoop {
// Safe to call before RegisterDelegateForCurrentThread(). // Safe to call before RegisterDelegateForCurrentThread().
static bool IsNestedOnCurrentThread(); static bool IsNestedOnCurrentThread();
// A NestingObserver is notified when a nested RunLoop begins. The observers // A NestingObserver is notified when a nested RunLoop begins and ends.
// are notified before the current thread's RunLoop::Delegate::Run() is
// invoked and nested work begins.
class BASE_EXPORT NestingObserver { class BASE_EXPORT NestingObserver {
public: public:
// Notified before a nested loop starts running work on the current thread.
virtual void OnBeginNestedRunLoop() = 0; virtual void OnBeginNestedRunLoop() = 0;
// Notified after a nested loop is done running work on the current thread.
virtual void OnExitNestedRunLoop() {}
protected: protected:
virtual ~NestingObserver() = default; virtual ~NestingObserver() = default;
......
...@@ -570,21 +570,37 @@ TEST_P(RunLoopTest, IsNestedOnCurrentThread) { ...@@ -570,21 +570,37 @@ TEST_P(RunLoopTest, IsNestedOnCurrentThread) {
run_loop_.Run(); run_loop_.Run();
} }
namespace {
class MockNestingObserver : public RunLoop::NestingObserver { class MockNestingObserver : public RunLoop::NestingObserver {
public: public:
MockNestingObserver() = default; MockNestingObserver() = default;
// RunLoop::NestingObserver: // RunLoop::NestingObserver:
MOCK_METHOD0(OnBeginNestedRunLoop, void()); MOCK_METHOD0(OnBeginNestedRunLoop, void());
MOCK_METHOD0(OnExitNestedRunLoop, void());
private: private:
DISALLOW_COPY_AND_ASSIGN(MockNestingObserver); DISALLOW_COPY_AND_ASSIGN(MockNestingObserver);
}; };
class MockTask {
public:
MockTask() = default;
MOCK_METHOD0(Task, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockTask);
};
} // namespace
TEST_P(RunLoopTest, NestingObservers) { TEST_P(RunLoopTest, NestingObservers) {
EXPECT_TRUE(RunLoop::IsNestingAllowedOnCurrentThread()); EXPECT_TRUE(RunLoop::IsNestingAllowedOnCurrentThread());
testing::StrictMock<MockNestingObserver> nesting_observer; testing::StrictMock<MockNestingObserver> nesting_observer;
testing::StrictMock<MockTask> mock_task_a;
testing::StrictMock<MockTask> mock_task_b;
RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer); RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer);
...@@ -599,14 +615,27 @@ TEST_P(RunLoopTest, NestingObservers) { ...@@ -599,14 +615,27 @@ TEST_P(RunLoopTest, NestingObservers) {
nested_run_loop.Run(); nested_run_loop.Run();
}); });
// Generate a stack of nested RunLoops, an OnBeginNestedRunLoop() is // Generate a stack of nested RunLoops. OnBeginNestedRunLoop() is expected
// expected when beginning each nesting depth. // when beginning each nesting depth and OnExitNestedRunLoop() is expected
// when exiting each nesting depth.
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop); ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a)));
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop); ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_nested_loop);
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop_.QuitClosure()); ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_b)));
EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop()).Times(2); {
run_loop_.Run(); testing::InSequence in_sequence;
EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
EXPECT_CALL(mock_task_a, Task());
EXPECT_CALL(nesting_observer, OnBeginNestedRunLoop());
EXPECT_CALL(mock_task_b, Task());
EXPECT_CALL(nesting_observer, OnExitNestedRunLoop()).Times(2);
}
run_loop_.RunUntilIdle();
RunLoop::RemoveNestingObserverOnCurrentThread(&nesting_observer); RunLoop::RemoveNestingObserverOnCurrentThread(&nesting_observer);
} }
......
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