Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3715)

Unified Diff: cc/scheduler/scheduler_unittest.cc

Issue 1139613003: cc: Avoid deadlock with the UI thread and NPAPI in more cases (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: cc/scheduler/scheduler_unittest.cc
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 2f878fdcdbfa9a22a3d101934c3200fb3c30b96d..6c629213245f76a3cb6bc0523766e3c49b493935 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -1378,7 +1378,14 @@ TEST_F(SchedulerTest, NotSkipMainFrameInPreferImplLatencyMode) {
EXPECT_SCOPED(MainFrameInHighLatencyMode(1, 1, true, true));
}
-TEST_F(SchedulerTest, PollForCommitCompletion) {
+TEST_F(SchedulerTest,
+ Deadlock_NotifyReadyToCommitMakesProgressWhileSwapTrottled) {
sunnyps 2015/05/14 00:02:02 Does this allow us to remove commit polling?
brianderson 2015/05/14 01:12:03 We can remove commit polling once TextureUploader
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main
+ // thread. This prevents the scheduler from receiving any pending swap acks.
+ // This test makes sure that we keep updating the TextureUploader with
+ // DidAnticipatedDrawTimeChange's so that it can make forward progress and
+ // upload all the textures needed for the commit to complete.
+
// Since we are simulating a long commit, set up a client with draw duration
// estimates that prevent skipping main frames to get to low latency mode.
SchedulerClientWithFixedEstimates* client =
@@ -1439,6 +1446,284 @@ TEST_F(SchedulerTest, PollForCommitCompletion) {
}
}
+TEST_F(
+ SchedulerTest,
+ Deadlock_CommitMakesProgressWhileSwapTrottledAndActiveTreeNeedsFirstDraw) {
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main
+ // thread. This prevents the scheduler from receiving any pending swap acks.
+ // The scheduler normally prioritizes draws over the commits, but doing so
+ // unconditionally will result in deadlocks.
+
+ // Since we are simulating a long commit, set up a client with draw duration
+ // estimates that prevent skipping main frames to get to low latency mode.
+ SchedulerClientWithFixedEstimates* client =
+ new SchedulerClientWithFixedEstimates(
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(32),
+ base::TimeDelta::FromMilliseconds(32));
+ scheduler_settings_.use_external_begin_frame_source = true;
+ scheduler_settings_.main_frame_while_swap_throttled_enabled = true;
+ scheduler_settings_.impl_side_painting = true;
+ SetUpScheduler(make_scoped_ptr(client).Pass(), true);
+
+ // Disables automatic swap acks so this test can force swap ack throttling
+ // to simulate a blocked Browser ui thread.
+ scheduler_->SetMaxSwapsPending(1);
+ client_->SetAutomaticSwapAck(false);
+
+ // Get a new active tree in main-thread high latency mode and put us
+ // in a swap throttled state.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ scheduler_->SetNeedsRedraw();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_TRUE(scheduler_->CommitPending());
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner().RunPendingTasks(); // Run posted deadlines.
sunnyps 2015/05/14 00:02:02 RunTasksWhile(DeadlinePending).
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ scheduler_->NotifyBeginMainFrameStarted();
+ scheduler_->NotifyReadyToCommit();
+ scheduler_->NotifyReadyToActivate();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame"));
sunnyps 2015/05/14 00:02:01 EXPECT_ACTION?
+ EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionCommit"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionActivateSyncTree"));
+
+ // Make sure that we can finish the next commit even while swap throttled.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ EXPECT_SCOPED(AdvanceFrame());
+ scheduler_->NotifyBeginMainFrameStarted();
+ scheduler_->NotifyReadyToCommit();
+ scheduler_->NotifyReadyToActivate();
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner().RunPendingTasks(); // Run posted deadlines.
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame"));
sunnyps 2015/05/14 00:02:02 EXPECT_ACTION?
+ EXPECT_TRUE(client->HasAction("ScheduledActionCommit"));
+ EXPECT_FALSE(client->HasAction("ScheduledActionActivateSyncTree"));
+
+ // Make sure we do not send a BeginMainFrame while swap throttled and
+ // we have both a pending tree and an active tree.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_FALSE(scheduler_->CommitPending());
+ task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_FALSE(client->HasAction("ScheduledActionSendBeginMainFrame"));
+}
+
+TEST_F(SchedulerTest,
+ Deadlock_CommitMakesProgressWhenIdleAndActiveTreeNeedsFirstDraw) {
sunnyps 2015/05/14 00:02:02 I think this test is for the "HavePendingTree" cas
brianderson 2015/05/14 01:12:03 It's also both. Will rename.
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main
+ // thread. This prevents the scheduler from receiving any pending swap acks.
+ // The scheduler normally prioritizes draws over the commits, but doing so
+ // unconditionally will result in deadlocks.
+
+ // Since we are simulating a long commit, set up a client with draw duration
+ // estimates that prevent skipping main frames to get to low latency mode.
+ SchedulerClientWithFixedEstimates* client =
+ new SchedulerClientWithFixedEstimates(
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(32),
+ base::TimeDelta::FromMilliseconds(32));
+ scheduler_settings_.use_external_begin_frame_source = true;
+ scheduler_settings_.main_frame_while_swap_throttled_enabled = true;
+ scheduler_settings_.main_frame_before_activation_enabled = true;
+ scheduler_settings_.impl_side_painting = true;
+ SetUpScheduler(make_scoped_ptr(client).Pass(), true);
+
+ // Disables automatic swap acks so this test can force swap ack throttling
+ // to simulate a blocked Browser ui thread.
+ scheduler_->SetMaxSwapsPending(1);
+ client_->SetAutomaticSwapAck(false);
+
+ // Start a new commit in main-thread high latency mode and hold off on
+ // activation.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ scheduler_->SetNeedsRedraw();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_TRUE(scheduler_->CommitPending());
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner().RunPendingTasks(); // Run posted deadlines.
sunnyps 2015/05/14 00:02:02 RunTasksWhile(DeadlinePending).
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ scheduler_->DidSwapBuffersComplete();
+ scheduler_->NotifyBeginMainFrameStarted();
+ scheduler_->NotifyReadyToCommit();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame"));
sunnyps 2015/05/14 00:02:01 EXPECT_ACTION?
+ EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionCommit"));
+ EXPECT_FALSE(client->HasAction("ScheduledActionActivateSyncTree"));
+
+ // Start another commit while we still have an active tree.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ scheduler_->SetNeedsRedraw();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame"));
+ EXPECT_TRUE(scheduler_->CommitPending());
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner().RunPendingTasks(); // Run posted deadlines.
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ scheduler_->DidSwapBuffersComplete();
+ scheduler_->NotifyBeginMainFrameStarted();
+
+ // Can't commit yet because there's still a pending tree.
+ scheduler_->NotifyReadyToCommit();
+ EXPECT_FALSE(client->HasAction("ScheduledActionActivateSyncTree"));
+ EXPECT_FALSE(client->HasAction("ScheduledActionCommit"));
+
+ // Activate the pending tree, which also unblocks the commit immediately.
+ scheduler_->NotifyReadyToActivate();
+ EXPECT_TRUE(client->HasAction("ScheduledActionActivateSyncTree"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionCommit"));
+}
+
+TEST_F(SchedulerTest,
+ Deadlock_NoBeginMainFrameWhileSwapTrottledAndHavePendingTree) {
sunnyps 2015/05/14 00:02:02 This is the "ActiveTreeNeedsFirstDraw" case.
brianderson 2015/05/14 01:12:03 It's both, I will change the name to: Deadlock_NoB
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main
+ // thread. This prevents the scheduler from receiving any pending swap acks.
+ // The scheduler normally prioritizes draws over the commits, but doing so
+ // unconditionally will result in deadlocks.
+
+ // Since we are simulating a long commit, set up a client with draw duration
+ // estimates that prevent skipping main frames to get to low latency mode.
+ SchedulerClientWithFixedEstimates* client =
+ new SchedulerClientWithFixedEstimates(
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(32),
+ base::TimeDelta::FromMilliseconds(32));
+ scheduler_settings_.use_external_begin_frame_source = true;
+ scheduler_settings_.main_frame_while_swap_throttled_enabled = true;
+ scheduler_settings_.main_frame_before_activation_enabled = true;
+ scheduler_settings_.impl_side_painting = true;
+ SetUpScheduler(make_scoped_ptr(client).Pass(), true);
+
+ // Disables automatic swap acks so this test can force swap ack throttling
+ // to simulate a blocked Browser ui thread.
+ scheduler_->SetMaxSwapsPending(1);
+ client_->SetAutomaticSwapAck(false);
+
+ // Start a new commit in main-thread high latency mode and hold off on
+ // activation.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ scheduler_->SetNeedsRedraw();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_TRUE(scheduler_->CommitPending());
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner().RunPendingTasks(); // Run posted deadlines.
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ scheduler_->DidSwapBuffersComplete();
+ scheduler_->NotifyBeginMainFrameStarted();
+ scheduler_->NotifyReadyToCommit();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionCommit"));
+ EXPECT_FALSE(client->HasAction("ScheduledActionActivateSyncTree"));
+
+ // Start another commit while we still have an active tree.
+ // Enter a swap throttled state.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ scheduler_->SetNeedsRedraw();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame"));
+ EXPECT_TRUE(scheduler_->CommitPending());
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner().RunPendingTasks(); // Run posted deadlines.
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ scheduler_->NotifyBeginMainFrameStarted();
+
+ // Can't commit yet because there's still a pending tree.
+ scheduler_->NotifyReadyToCommit();
+ EXPECT_FALSE(client->HasAction("ScheduledActionActivateSyncTree"));
+ EXPECT_FALSE(client->HasAction("ScheduledActionCommit"));
+
+ // Activate the pending tree, which also unblocks the commit immediately.
+ scheduler_->NotifyReadyToActivate();
+ EXPECT_TRUE(client->HasAction("ScheduledActionActivateSyncTree"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionCommit"));
+
+ // Make sure we do not send a BeginMainFrame while swap throttled and
+ // we have both a pending tree and an active tree that still needs
+ // it's first draw.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_FALSE(scheduler_->CommitPending());
+ task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_FALSE(client->HasAction("ScheduledActionSendBeginMainFrame"));
+}
+
+TEST_F(SchedulerTest,
+ Deadlock_NoBeginMainFrameWhileSwapTrottledAndActiveTreeNeedsFirstDraw) {
sunnyps 2015/05/14 00:02:02 In this case BeginMainFrame is blocked because of
brianderson 2015/05/14 01:12:03 Sorry, my comments in the test were wrong. This te
+ // This test only applies with non-impl-side painting, where we do not have a
+ // pending tree and might not be able to commit if we send a BeginMainFrame.
+
+ // NPAPI plugins on Windows block the Browser UI thread on the Renderer main
+ // thread. This prevents the scheduler from receiving any pending swap acks.
+ // The scheduler normally prioritizes draws over the commits, but doing so
+ // unconditionally will result in deadlocks.
+
+ // Since we are simulating a long commit, set up a client with draw duration
+ // estimates that prevent skipping main frames to get to low latency mode.
+ SchedulerClientWithFixedEstimates* client =
+ new SchedulerClientWithFixedEstimates(
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromMilliseconds(32),
+ base::TimeDelta::FromMilliseconds(32));
+ scheduler_settings_.use_external_begin_frame_source = true;
+ scheduler_settings_.impl_side_painting = false;
+ SetUpScheduler(make_scoped_ptr(client).Pass(), true);
+
+ // Disables automatic swap acks so this test can force swap ack throttling
+ // to simulate a blocked Browser ui thread.
+ scheduler_->SetMaxSwapsPending(1);
+ client_->SetAutomaticSwapAck(false);
+
+ // Get a new active tree in main-thread high latency mode.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ scheduler_->SetNeedsRedraw();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_TRUE(scheduler_->CommitPending());
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner().RunPendingTasks(); // Run posted deadlines.
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ scheduler_->NotifyBeginMainFrameStarted();
+ scheduler_->NotifyReadyToCommit();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ EXPECT_TRUE(client->HasAction("ScheduledActionSendBeginMainFrame"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionDrawAndSwapIfPossible"));
+ EXPECT_TRUE(client->HasAction("ScheduledActionCommit"));
+
+ // Make sure we do not send a BeginMainFrame while swap throttled and
+ // we have both a pending tree and an active tree.
+ client_->Reset();
+ EXPECT_FALSE(scheduler_->CommitPending());
+ scheduler_->SetNeedsCommit();
+ EXPECT_SCOPED(AdvanceFrame());
+ EXPECT_FALSE(scheduler_->CommitPending());
+ task_runner().RunPendingTasks(); // Run posted deadline.
+ EXPECT_FALSE(client->HasAction("ScheduledActionSendBeginMainFrame"));
+}
+
TEST_F(SchedulerTest, BeginRetroFrame) {
scheduler_settings_.use_external_begin_frame_source = true;
SetUpScheduler(true);
« cc/scheduler/scheduler_state_machine.cc ('K') | « cc/scheduler/scheduler_state_machine.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698