Commit 2bcaa68b authored by brianderson's avatar brianderson Committed by Commit bot

Avoid deadlock with the UI thread and NPAPI in more cases

By doing the following:
1) Avoid deferring commits until draw if not drawing soon.
2) Don't send BeginMainFrame if swap throttling will block commit.

As a side-effect, some of the changes will also help release
the main thread sooner in some cases where we were holding on to
it for too long.

Additional scheduler deadlock tests added to verify existing
and future operation.

This should also enable us to more easily avoid unconditionally
setting active_tree_needs_first_draw_ to false in
SchedulerStateMachine::UpdateStateOnInvalidateOutputSurface
in followup patches if we want to.

BUG=479671, 477082

Review URL: https://codereview.chromium.org/1139613003

Cr-Commit-Position: refs/heads/master@{#330808}
parent 49236cbd
......@@ -10,6 +10,7 @@ namespace cc {
SchedulerSettings::SchedulerSettings()
: use_external_begin_frame_source(false),
main_frame_while_swap_throttled_enabled(false),
main_frame_before_activation_enabled(false),
impl_side_painting(false),
timeout_and_draw_when_animation_checkerboards(true),
......@@ -28,6 +29,8 @@ SchedulerSettings::AsValue() const {
new base::trace_event::TracedValue();
state->SetBoolean("use_external_begin_frame_source",
use_external_begin_frame_source);
state->SetBoolean("main_frame_while_swap_throttled_enabled",
main_frame_while_swap_throttled_enabled);
state->SetBoolean("main_frame_before_activation_enabled",
main_frame_before_activation_enabled);
state->SetBoolean("impl_side_painting", impl_side_painting);
......
......@@ -24,6 +24,7 @@ class CC_EXPORT SchedulerSettings {
~SchedulerSettings();
bool use_external_begin_frame_source;
bool main_frame_while_swap_throttled_enabled;
bool main_frame_before_activation_enabled;
bool impl_side_painting;
bool timeout_and_draw_when_animation_checkerboards;
......
......@@ -402,6 +402,19 @@ bool SchedulerStateMachine::CouldSendBeginMainFrame() const {
return true;
}
bool SchedulerStateMachine::SendingBeginMainFrameMightCauseDeadlock() const {
// NPAPI is the only case where the UI thread makes synchronous calls to the
// Renderer main thread. During that synchronous call, we may not get a
// SwapAck for the UI thread, which may prevent BeginMainFrame's from
// completing if there's enough back pressure. If the BeginMainFrame can't
// make progress, the Renderer can't service the UI thread's synchronous call
// and we have deadlock.
// This returns true if there's too much backpressure to finish a commit
// if we were to initiate a BeginMainFrame.
return has_pending_tree_ && active_tree_needs_first_draw_ &&
pending_swaps_ >= max_pending_swaps_;
}
bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
if (!CouldSendBeginMainFrame())
return false;
......@@ -411,6 +424,9 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
return false;
// Only send BeginMainFrame when there isn't another commit pending already.
// Other parts of the state machine indirectly defer the BeginMainFrame
// by transitioning to WAITING commit states rather than going
// immediately to IDLE.
if (commit_state_ != COMMIT_STATE_IDLE)
return false;
......@@ -432,6 +448,9 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
// We need a new commit for the forced redraw. This honors the
// single commit per interval because the result will be swapped to screen.
// TODO(brianderson): Remove this or move it below the
// SendingBeginMainFrameMightCauseDeadlock check since we want to avoid
// ever returning true from this method if we might cause deadlock.
if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_COMMIT)
return true;
......@@ -439,14 +458,22 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
if (!HasInitializedOutputSurface())
return false;
// SwapAck throttle the BeginMainFrames unless we just swapped.
// TODO(brianderson): Remove this restriction to improve throughput.
bool just_swapped_in_deadline =
begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
did_perform_swap_in_last_draw_;
if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline)
// Make sure the BeginMainFrame can finish eventually if we start it.
if (SendingBeginMainFrameMightCauseDeadlock())
return false;
if (!settings_.main_frame_while_swap_throttled_enabled) {
// SwapAck throttle the BeginMainFrames unless we just swapped to
// potentially improve impl-thread latency over main-thread throughput.
// TODO(brianderson): Remove this restriction to improve throughput or
// make it conditional on impl_latency_takes_priority_.
bool just_swapped_in_deadline =
begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
did_perform_swap_in_last_draw_;
if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline)
return false;
}
if (skip_begin_main_frame_to_reduce_latency_)
return false;
......@@ -463,11 +490,14 @@ bool SchedulerStateMachine::ShouldCommit() const {
return false;
}
// Prioritize drawing the previous commit before finishing the next commit.
if (active_tree_needs_first_draw_)
return false;
// If impl-side-painting, commit to the pending tree as soon as we can.
if (settings_.impl_side_painting)
return true;
return true;
// If we only have an active tree, it is incorrect to replace it
// before we've drawn it when we aren't impl-side-painting.
DCHECK(!settings_.impl_side_painting);
return !active_tree_needs_first_draw_;
}
bool SchedulerStateMachine::ShouldPrepareTiles() const {
......
......@@ -268,6 +268,9 @@ class CC_EXPORT SchedulerStateMachine {
// True if we need to force activations to make forward progress.
bool PendingActivationsShouldBeForced() const;
// TODO(brianderson): Remove this once NPAPI support is removed.
bool SendingBeginMainFrameMightCauseDeadlock() const;
bool ShouldAnimate() const;
bool ShouldBeginOutputSurfaceCreation() const;
bool ShouldDraw() const;
......
......@@ -331,10 +331,11 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) {
state.NotifyReadyToCommit();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
// Verify NotifyReadyToActivate unblocks activation, draw, and
// commit in that order.
// Verify NotifyReadyToActivate unblocks activation, commit, and
// draw in that order.
state.NotifyReadyToActivate();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
......@@ -344,7 +345,6 @@ TEST(SchedulerStateMachineTest, MainFrameBeforeActivationEnabled) {
SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE);
state.DidSwapBuffers();
state.DidSwapBuffersComplete();
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
EXPECT_COMMIT_STATE(SchedulerStateMachine::COMMIT_STATE_IDLE);
}
......
This diff is collapsed.
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