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/766388
Reviewed-by: Gabriel Charette <[email protected]>
Reviewed-by: Alexander Timin <[email protected]>
Commit-Queue: François Doray <[email protected]>
Cr-Commit-Position: refs/heads/master@{#527112}
diff --git a/base/run_loop.cc b/base/run_loop.cc
index 3cda1cb0..3b7bfde 100644
--- a/base/run_loop.cc
+++ b/base/run_loop.cc
@@ -321,6 +321,11 @@
RunLoop* previous_run_loop =
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:
if (previous_run_loop && previous_run_loop->quit_called_)
delegate_->Quit();
diff --git a/base/run_loop.h b/base/run_loop.h
index e43c4fc..b7c594e1 100644
--- a/base/run_loop.h
+++ b/base/run_loop.h
@@ -130,12 +130,13 @@
// Safe to call before RegisterDelegateForCurrentThread().
static bool IsNestedOnCurrentThread();
- // A NestingObserver is notified when a nested RunLoop begins. The observers
- // are notified before the current thread's RunLoop::Delegate::Run() is
- // invoked and nested work begins.
+ // A NestingObserver is notified when a nested RunLoop begins and ends.
class BASE_EXPORT NestingObserver {
public:
+ // Notified before a nested loop starts running work on the current thread.
virtual void OnBeginNestedRunLoop() = 0;
+ // Notified after a nested loop is done running work on the current thread.
+ virtual void OnExitNestedRunLoop() {}
protected:
virtual ~NestingObserver() = default;
diff --git a/base/run_loop_unittest.cc b/base/run_loop_unittest.cc
index 714bf82..96060f4a 100644
--- a/base/run_loop_unittest.cc
+++ b/base/run_loop_unittest.cc
@@ -570,21 +570,37 @@
run_loop_.Run();
}
+namespace {
+
class MockNestingObserver : public RunLoop::NestingObserver {
public:
MockNestingObserver() = default;
// RunLoop::NestingObserver:
MOCK_METHOD0(OnBeginNestedRunLoop, void());
+ MOCK_METHOD0(OnExitNestedRunLoop, void());
private:
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) {
EXPECT_TRUE(RunLoop::IsNestingAllowedOnCurrentThread());
testing::StrictMock<MockNestingObserver> nesting_observer;
+ testing::StrictMock<MockTask> mock_task_a;
+ testing::StrictMock<MockTask> mock_task_b;
RunLoop::AddNestingObserverOnCurrentThread(&nesting_observer);
@@ -599,14 +615,27 @@
nested_run_loop.Run();
});
- // Generate a stack of nested RunLoops, an OnBeginNestedRunLoop() is
- // expected when beginning each nesting depth.
+ // Generate a stack of nested RunLoops. OnBeginNestedRunLoop() is expected
+ // 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,
+ base::BindOnce(&MockTask::Task, base::Unretained(&mock_task_a)));
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);
}