Commit 3369eecd authored by Gil Dekel's avatar Gil Dekel Committed by Commit Bot

UI/Display: Simplify and Clean Up ConfigureDisplayTask

This CL simplifies and cleans up the way ConfigureDisplayTask manages
its state over sync/async callbacks to reduce complexity and race-risk
by tightening up the way calls to Run() and OnConfigure() are handled.

In addition, this CL adds a test to ensure that we attempt to
reconfigure displays when any of them fail, not just the last one.

Finally, it removes ConfigureDisplaysTaskTest.ConfigureWithNoDisplays,
since this case should never occur in reality.

TEST=display_unittests

Change-Id: Icf34b00205e980df2bb5facbe6da5387a3e9dce3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2422559
Commit-Queue: Gil Dekel <gildekel@chromium.org>
Reviewed-by: default avatarDaniel Nicoara <dnicoara@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814355}
parent a9caf7c0
...@@ -96,11 +96,7 @@ ConfigureDisplaysTask::ConfigureDisplaysTask( ...@@ -96,11 +96,7 @@ ConfigureDisplaysTask::ConfigureDisplaysTask(
: delegate_(delegate), : delegate_(delegate),
requests_(requests), requests_(requests),
callback_(std::move(callback)), callback_(std::move(callback)),
is_configuring_(false),
num_displays_configured_(0),
task_status_(SUCCESS) { task_status_(SUCCESS) {
for (size_t i = 0; i < requests_.size(); ++i)
pending_request_indexes_.push(i);
delegate_->AddObserver(this); delegate_->AddObserver(this);
} }
...@@ -109,37 +105,19 @@ ConfigureDisplaysTask::~ConfigureDisplaysTask() { ...@@ -109,37 +105,19 @@ ConfigureDisplaysTask::~ConfigureDisplaysTask() {
} }
void ConfigureDisplaysTask::Run() { void ConfigureDisplaysTask::Run() {
// Synchronous configurators will recursively call Run(). In that case just DCHECK(!requests_.empty());
// defer their call to the next iteration in the while-loop. This is done to
// guard against stack overflows if the display has a large list of broken
// modes.
if (is_configuring_)
return;
{
base::AutoReset<bool> recursivity_guard(&is_configuring_, true);
// The callback passed to delegate_->Configure() could be run synchronously
// or async. If it runs synchronously and any display fails and tries to
// reconfigure, new requests will get added to |pending_request_indexes_|,
// then another attempt to reconfigure will recursively call Run(). However
// |is_configuring_| will block the run due to the reason mentioned above.
// Hence, after we configure the first set of pending requests (loop over
// |current_pending_requests_count|), we check again if the new requests
// were added (!pending_request_indexes_.empty()).
while (!pending_request_indexes_.empty()) {
std::vector<display::DisplayConfigurationParams> config_requests; std::vector<display::DisplayConfigurationParams> config_requests;
size_t current_pending_requests_count = pending_request_indexes_.size(); for (const auto& request : requests_) {
for (size_t i = 0; i < current_pending_requests_count; ++i) { config_requests.emplace_back(request.display->display_id(), request.origin,
size_t index = pending_request_indexes_.front(); request.mode);
DisplayConfigureRequest* request = &requests_[index];
pending_request_indexes_.pop();
const bool internal = const bool internal =
request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
base::UmaHistogramExactLinear( base::UmaHistogramExactLinear(
internal ? "ConfigureDisplays.Internal.Modeset.Resolution" internal ? "ConfigureDisplays.Internal.Modeset.Resolution"
: "ConfigureDisplays.External.Modeset.Resolution", : "ConfigureDisplays.External.Modeset.Resolution",
ComputeDisplayResolutionEnum(request->mode), ComputeDisplayResolutionEnum(request.mode),
base::size(kDisplayResolutionSamples) * base::size(kDisplayResolutionSamples) *
base::size(kDisplayResolutionSamples) + base::size(kDisplayResolutionSamples) +
2); 2);
...@@ -147,43 +125,26 @@ void ConfigureDisplaysTask::Run() { ...@@ -147,43 +125,26 @@ void ConfigureDisplaysTask::Run() {
internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate" internal ? "ConfigureDisplays.Internal.Modeset.RefreshRate"
: "ConfigureDisplays.External.Modeset.RefreshRate", : "ConfigureDisplays.External.Modeset.RefreshRate",
1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag); 1, 240, 18, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(request->mode ? std::round(request->mode->refresh_rate()) histogram->Add(request.mode ? std::round(request.mode->refresh_rate()) : 0);
: 0);
display::DisplayConfigurationParams display_config_params(
request->display->display_id(), request->origin, request->mode);
config_requests.push_back(std::move(display_config_params));
} }
if (!config_requests.empty()) {
delegate_->Configure( delegate_->Configure(config_requests,
config_requests,
base::BindOnce(&ConfigureDisplaysTask::OnConfigured, base::BindOnce(&ConfigureDisplaysTask::OnConfigured,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
}
}
}
// Nothing should be modified after the |callback_| is called since the
// task may be deleted in the callback.
if (num_displays_configured_ == requests_.size())
std::move(callback_).Run(task_status_);
} }
void ConfigureDisplaysTask::OnConfigurationChanged() {} void ConfigureDisplaysTask::OnConfigurationChanged() {}
void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() { void ConfigureDisplaysTask::OnDisplaySnapshotsInvalidated() {
base::queue<size_t> empty_queue;
pending_request_indexes_.swap(empty_queue);
// From now on, don't access |requests_[index]->display|; they're invalid. // From now on, don't access |requests_[index]->display|; they're invalid.
task_status_ = ERROR; task_status_ = ERROR;
weak_ptr_factory_.InvalidateWeakPtrs(); weak_ptr_factory_.InvalidateWeakPtrs();
Run(); std::move(callback_).Run(task_status_);
} }
void ConfigureDisplaysTask::OnConfigured( void ConfigureDisplaysTask::OnConfigured(
const base::flat_map<int64_t, bool>& statuses) { const base::flat_map<int64_t, bool>& statuses) {
bool config_success = true; bool config_success = true;
// Check if all displays are successfully configured. // Check if all displays are successfully configured.
for (const auto& status : statuses) { for (const auto& status : statuses) {
int64_t display_id = status.first; int64_t display_id = status.first;
...@@ -206,11 +167,11 @@ void ConfigureDisplaysTask::OnConfigured( ...@@ -206,11 +167,11 @@ void ConfigureDisplaysTask::OnConfigured(
display_success); display_success);
} }
// Update displays upon success or prep |requests_| for reconfiguration.
if (config_success) { if (config_success) {
for (const auto& status : statuses) { for (auto& request : requests_) {
auto request = GetRequestForDisplayId(status.first, requests_); request.display->set_current_mode(request.mode);
request->display->set_current_mode(request->mode); request.display->set_origin(request.origin);
request->display->set_origin(request->origin);
} }
} else { } else {
bool should_reconfigure = false; bool should_reconfigure = false;
...@@ -230,37 +191,25 @@ void ConfigureDisplaysTask::OnConfigured( ...@@ -230,37 +191,25 @@ void ConfigureDisplaysTask::OnConfigured(
} }
} }
} }
// When reconfiguring, reconfigure all displays, not only the failing ones
// as they could potentially depend on each other.
if (should_reconfigure) { if (should_reconfigure) {
for (const auto& status : statuses) {
auto const_iterator = GetRequestForDisplayId(status.first, requests_);
auto request = requests_.erase(const_iterator, const_iterator);
size_t index = std::distance(requests_.begin(), request);
pending_request_indexes_.push(index);
}
if (task_status_ == SUCCESS)
task_status_ = PARTIAL_SUCCESS; task_status_ = PARTIAL_SUCCESS;
Run(); Run();
return; return;
} }
} }
// If no reconfigurations are happening, update the final state. // Update the final state.
for (const auto& status : statuses) { for (auto& request : requests_) {
auto request = GetRequestForDisplayId(status.first, requests_); bool internal = request.display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
bool internal =
request->display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
base::UmaHistogramBoolean( base::UmaHistogramBoolean(
internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus" internal ? "ConfigureDisplays.Internal.Modeset.FinalStatus"
: "ConfigureDisplays.External.Modeset.FinalStatus", : "ConfigureDisplays.External.Modeset.FinalStatus",
config_success); config_success);
} }
num_displays_configured_ += statuses.size();
if (!config_success) if (!config_success)
task_status_ = ERROR; task_status_ = ERROR;
Run(); std::move(callback_).Run(task_status_);
} }
} // namespace display } // namespace display
...@@ -76,17 +76,6 @@ class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask ...@@ -76,17 +76,6 @@ class DISPLAY_MANAGER_EXPORT ConfigureDisplaysTask
// task is done and the task status. // task is done and the task status.
ResponseCallback callback_; ResponseCallback callback_;
// Stores the indexes of pending requests in |requests_|.
base::queue<size_t> pending_request_indexes_;
// Used to keep make sure that synchronous executions do not recurse during
// the configuration.
bool is_configuring_;
// Number of display configured. This is used to check whether there are
// pending requests.
size_t num_displays_configured_;
Status task_status_; Status task_status_;
base::WeakPtrFactory<ConfigureDisplaysTask> weak_ptr_factory_{this}; base::WeakPtrFactory<ConfigureDisplaysTask> weak_ptr_factory_{this};
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include <stddef.h> #include <stddef.h>
#include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/run_loop.h" #include "base/run_loop.h"
...@@ -24,21 +25,22 @@ class ConfigureDisplaysTaskTest : public testing::Test { ...@@ -24,21 +25,22 @@ class ConfigureDisplaysTaskTest : public testing::Test {
ConfigureDisplaysTaskTest() ConfigureDisplaysTaskTest()
: delegate_(&log_), : delegate_(&log_),
small_mode_(gfx::Size(1366, 768), false, 60.0f), small_mode_(gfx::Size(1366, 768), false, 60.0f),
big_mode_(gfx::Size(2560, 1600), false, 60.0f) { big_mode_(gfx::Size(2560, 1600), false, 60.0f) {}
displays_[0] = FakeDisplaySnapshot::Builder() ~ConfigureDisplaysTaskTest() override = default;
void SetUp() override {
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(123) .SetId(123)
.SetNativeMode(small_mode_.Clone()) .SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone()) .SetCurrentMode(small_mode_.Clone())
.Build(); .Build());
displays_.push_back(FakeDisplaySnapshot::Builder()
displays_[1] = FakeDisplaySnapshot::Builder()
.SetId(456) .SetId(456)
.SetNativeMode(big_mode_.Clone()) .SetNativeMode(big_mode_.Clone())
.SetCurrentMode(big_mode_.Clone()) .SetCurrentMode(big_mode_.Clone())
.AddMode(small_mode_.Clone()) .AddMode(small_mode_.Clone())
.Build(); .Build());
} }
~ConfigureDisplaysTaskTest() override = default;
void ConfigureCallback(ConfigureDisplaysTask::Status status) { void ConfigureCallback(ConfigureDisplaysTask::Status status) {
callback_called_ = true; callback_called_ = true;
...@@ -56,7 +58,7 @@ class ConfigureDisplaysTaskTest : public testing::Test { ...@@ -56,7 +58,7 @@ class ConfigureDisplaysTaskTest : public testing::Test {
const DisplayMode small_mode_; const DisplayMode small_mode_;
const DisplayMode big_mode_; const DisplayMode big_mode_;
std::unique_ptr<DisplaySnapshot> displays_[2]; std::vector<std::unique_ptr<DisplaySnapshot>> displays_;
private: private:
DISALLOW_COPY_AND_ASSIGN(ConfigureDisplaysTaskTest); DISALLOW_COPY_AND_ASSIGN(ConfigureDisplaysTaskTest);
...@@ -64,20 +66,6 @@ class ConfigureDisplaysTaskTest : public testing::Test { ...@@ -64,20 +66,6 @@ class ConfigureDisplaysTaskTest : public testing::Test {
} // namespace } // namespace
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithNoDisplays) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
ConfigureDisplaysTask task(&delegate_, std::vector<DisplayConfigureRequest>(),
std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::SUCCESS, status_);
EXPECT_EQ(kNoActions, log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplay) { TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplay) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
...@@ -100,9 +88,8 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) { ...@@ -100,9 +88,8 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) {
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
std::vector<DisplayConfigureRequest> requests; std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) { for (const auto& display : displays_) {
requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(), requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
gfx::Point());
} }
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
...@@ -170,9 +157,8 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) { ...@@ -170,9 +157,8 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) {
delegate_.set_max_configurable_pixels(1); delegate_.set_max_configurable_pixels(1);
std::vector<DisplayConfigureRequest> requests; std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) { for (const auto& display : displays_) {
requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(), requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
gfx::Point());
} }
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
...@@ -196,16 +182,53 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) { ...@@ -196,16 +182,53 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) {
log_.GetActionsAndClear()); log_.GetActionsAndClear());
} }
TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplaysPartialSuccess) { TEST_F(ConfigureDisplaysTaskTest, ReconfigureLastDisplayPartialSuccess) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
delegate_.set_max_configurable_pixels(small_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests;
for (const auto& display : displays_) {
requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
}
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
task.Run();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(ConfigureDisplaysTask::PARTIAL_SUCCESS, status_);
EXPECT_EQ(JoinActions(GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&small_mode_})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
&big_mode_})
.c_str(),
GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&small_mode_})
.c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
&small_mode_})
.c_str(),
nullptr),
log_.GetActionsAndClear());
}
TEST_F(ConfigureDisplaysTaskTest, ReconfigureMiddleDisplayPartialSuccess) {
ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce(
&ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this));
displays_.push_back(FakeDisplaySnapshot::Builder()
.SetId(789)
.SetNativeMode(small_mode_.Clone())
.SetCurrentMode(small_mode_.Clone())
.Build());
delegate_.set_max_configurable_pixels(small_mode_.size().GetArea()); delegate_.set_max_configurable_pixels(small_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests; std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) { for (const auto& display : displays_) {
requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(), requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
gfx::Point());
} }
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
...@@ -219,12 +242,18 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplaysPartialSuccess) { ...@@ -219,12 +242,18 @@ TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplaysPartialSuccess) {
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
&big_mode_}) &big_mode_})
.c_str(), .c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(),
&small_mode_})
.c_str(),
GetCrtcAction({displays_[0]->display_id(), gfx::Point(), GetCrtcAction({displays_[0]->display_id(), gfx::Point(),
&small_mode_}) &small_mode_})
.c_str(), .c_str(),
GetCrtcAction({displays_[1]->display_id(), gfx::Point(), GetCrtcAction({displays_[1]->display_id(), gfx::Point(),
&small_mode_}) &small_mode_})
.c_str(), .c_str(),
GetCrtcAction({displays_[2]->display_id(), gfx::Point(),
&small_mode_})
.c_str(),
nullptr), nullptr),
log_.GetActionsAndClear()); log_.GetActionsAndClear());
} }
...@@ -237,9 +266,8 @@ TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) { ...@@ -237,9 +266,8 @@ TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) {
delegate_.set_max_configurable_pixels(small_mode_.size().GetArea()); delegate_.set_max_configurable_pixels(small_mode_.size().GetArea());
std::vector<DisplayConfigureRequest> requests; std::vector<DisplayConfigureRequest> requests;
for (size_t i = 0; i < base::size(displays_); ++i) { for (const auto& display : displays_) {
requests.emplace_back(displays_[i].get(), displays_[i]->native_mode(), requests.emplace_back(display.get(), display->native_mode(), gfx::Point());
gfx::Point());
} }
ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); ConfigureDisplaysTask task(&delegate_, requests, std::move(callback));
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/task_runner.h" #include "base/task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "ui/display/types/display_snapshot.h" #include "ui/display/types/display_snapshot.h"
...@@ -139,7 +140,12 @@ void HostDrmDevice::GpuConfigureNativeDisplays( ...@@ -139,7 +140,12 @@ void HostDrmDevice::GpuConfigureNativeDisplays(
base::flat_map<int64_t, bool> dummy_statuses; base::flat_map<int64_t, bool> dummy_statuses;
for (const auto& config : config_requests) for (const auto& config : config_requests)
dummy_statuses.insert(std::make_pair(config.id, false)); dummy_statuses.insert(std::make_pair(config.id, false));
std::move(callback).Run(dummy_statuses);
// Post this task to protect the callstack from accumulating too many
// recursive calls to ConfigureDisplaysTask::Run() in cases in which the GPU
// process crashes repeatedly.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), dummy_statuses));
} }
} }
......
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