Commit 5bb676be authored by Kramer Ge's avatar Kramer Ge Committed by Commit Bot

Relax RunTaskAfterWindowReady to block only on drm_device

Browser UI thread can be blocked on viz compositor or viz main. RunTaskAfterWindowReady blocks viz main on DRM thread when it is posted by PostSyncTask. These tasks need browser to call CreateWindow through mojo pipe to continue.
Deadlocks are caused by this. RunTaskAfterWindowReady needs to be relaxed to lower crash rates.

Bug: 1050676
Change-Id: Ie205300fa68300f129c1bb1e7d746c6399a65013
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2135128
Commit-Queue: Kramer Ge <fangzhoug@chromium.org>
Reviewed-by: default avatarMichael Spang <spang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#756756}
parent 73144653
...@@ -92,17 +92,15 @@ void DrmThread::Start(base::OnceClosure receiver_completer, ...@@ -92,17 +92,15 @@ void DrmThread::Start(base::OnceClosure receiver_completer,
LOG(FATAL) << "Failed to create DRM thread"; LOG(FATAL) << "Failed to create DRM thread";
} }
void DrmThread::RunTaskAfterWindowReady(gfx::AcceleratedWidget window, void DrmThread::RunTaskAfterDeviceReady(base::OnceClosure task,
base::OnceClosure task,
base::WaitableEvent* done) { base::WaitableEvent* done) {
if (!device_manager_->GetDrmDevices().empty() && if (!device_manager_->GetDrmDevices().empty()) {
window <= last_created_window_) {
std::move(task).Run(); std::move(task).Run();
if (done) if (done)
done->Signal(); done->Signal();
return; return;
} }
pending_tasks_[window].emplace_back(std::move(task), done); pending_tasks_.emplace_back(std::move(task), done);
} }
void DrmThread::Init() { void DrmThread::Init() {
...@@ -411,17 +409,13 @@ void DrmThread::AddDrmDeviceReceiver( ...@@ -411,17 +409,13 @@ void DrmThread::AddDrmDeviceReceiver(
void DrmThread::ProcessPendingTasks() { void DrmThread::ProcessPendingTasks() {
DCHECK(!device_manager_->GetDrmDevices().empty()); DCHECK(!device_manager_->GetDrmDevices().empty());
auto it = pending_tasks_.begin(); for (auto& task_info : pending_tasks_) {
for (; it != pending_tasks_.end() && it->first <= last_created_window_;
++it) {
for (auto& task_info : it->second) {
std::move(task_info.task).Run(); std::move(task_info.task).Run();
if (task_info.done) if (task_info.done)
task_info.done->Signal(); task_info.done->Signal();
} }
}
pending_tasks_.erase(pending_tasks_.begin(), it); pending_tasks_.clear();
} }
} // namespace ui } // namespace ui
...@@ -74,10 +74,9 @@ class DrmThread : public base::Thread, ...@@ -74,10 +74,9 @@ class DrmThread : public base::Thread,
void Start(base::OnceClosure receiver_completer, void Start(base::OnceClosure receiver_completer,
std::unique_ptr<DrmDeviceGenerator> device_generator); std::unique_ptr<DrmDeviceGenerator> device_generator);
// Runs |task| once a DrmDevice is registered and |window| was created via // Runs |task| once a DrmDevice is registered. |done|
// CreateWindow(). |done| will be signaled if it's not null. // will be signaled if it's not null.
void RunTaskAfterWindowReady(gfx::AcceleratedWidget window, void RunTaskAfterDeviceReady(base::OnceClosure task,
base::OnceClosure task,
base::WaitableEvent* done); base::WaitableEvent* done);
// Must be called on the DRM thread. All methods for use from the GPU thread. // Must be called on the DRM thread. All methods for use from the GPU thread.
...@@ -218,9 +217,8 @@ class DrmThread : public base::Thread, ...@@ -218,9 +217,8 @@ class DrmThread : public base::Thread,
// The AcceleratedWidget from the last call to CreateWindow. // The AcceleratedWidget from the last call to CreateWindow.
gfx::AcceleratedWidget last_created_window_ = gfx::kNullAcceleratedWidget; gfx::AcceleratedWidget last_created_window_ = gfx::kNullAcceleratedWidget;
// The tasks that are blocked on a DrmDevice and a certain AcceleratedWidget // The tasks that are blocked on a DrmDevice becoming available.
// becoming available. std::vector<TaskInfo> pending_tasks_;
base::flat_map<gfx::AcceleratedWidget, std::vector<TaskInfo>> pending_tasks_;
// Holds the DrmDeviceGenerator that DrmDeviceManager will use. Will be passed // Holds the DrmDeviceGenerator that DrmDeviceManager will use. Will be passed
// on to DrmDeviceManager after the thread starts. // on to DrmDeviceManager after the thread starts.
......
...@@ -93,10 +93,9 @@ void DrmThreadProxy::CreateBuffer(gfx::AcceleratedWidget widget, ...@@ -93,10 +93,9 @@ void DrmThreadProxy::CreateBuffer(gfx::AcceleratedWidget widget,
base::OnceClosure task = base::BindOnce( base::OnceClosure task = base::BindOnce(
&DrmThread::CreateBuffer, base::Unretained(&drm_thread_), widget, size, &DrmThread::CreateBuffer, base::Unretained(&drm_thread_), widget, size,
framebuffer_size, format, usage, flags, buffer, framebuffer); framebuffer_size, format, usage, flags, buffer, framebuffer);
PostSyncTask( PostSyncTask(drm_thread_.task_runner(),
drm_thread_.task_runner(), base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
base::BindOnce(&DrmThread::RunTaskAfterWindowReady, base::Unretained(&drm_thread_), std::move(task)));
base::Unretained(&drm_thread_), widget, std::move(task)));
} }
void DrmThreadProxy::CreateBufferAsync(gfx::AcceleratedWidget widget, void DrmThreadProxy::CreateBufferAsync(gfx::AcceleratedWidget widget,
...@@ -112,15 +111,10 @@ void DrmThreadProxy::CreateBufferAsync(gfx::AcceleratedWidget widget, ...@@ -112,15 +111,10 @@ void DrmThreadProxy::CreateBufferAsync(gfx::AcceleratedWidget widget,
size, format, usage, flags, size, format, usage, flags,
base::BindOnce(OnBufferCreatedOnDrmThread, base::BindOnce(OnBufferCreatedOnDrmThread,
base::ThreadTaskRunnerHandle::Get(), std::move(callback))); base::ThreadTaskRunnerHandle::Get(), std::move(callback)));
// Since browser's UI thread blocks until a buffer is returned, we shouldn't
// block on |widget| because a blocked UI thread cannot register |widget| and
// causes a deadlock. We still want to block on a graphics device, though.
// TODO(samans): Remove this hack once OOP-D launches.
gfx::AcceleratedWidget blocking_widget = gfx::kNullAcceleratedWidget;
drm_thread_.task_runner()->PostTask( drm_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady, FROM_HERE,
base::Unretained(&drm_thread_), blocking_widget, base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
std::move(task), nullptr)); base::Unretained(&drm_thread_), std::move(task), nullptr));
} }
void DrmThreadProxy::CreateBufferFromHandle( void DrmThreadProxy::CreateBufferFromHandle(
...@@ -135,10 +129,9 @@ void DrmThreadProxy::CreateBufferFromHandle( ...@@ -135,10 +129,9 @@ void DrmThreadProxy::CreateBufferFromHandle(
base::OnceClosure task = base::BindOnce( base::OnceClosure task = base::BindOnce(
&DrmThread::CreateBufferFromHandle, base::Unretained(&drm_thread_), &DrmThread::CreateBufferFromHandle, base::Unretained(&drm_thread_),
widget, size, format, std::move(handle), buffer, framebuffer); widget, size, format, std::move(handle), buffer, framebuffer);
PostSyncTask( PostSyncTask(drm_thread_.task_runner(),
drm_thread_.task_runner(), base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
base::BindOnce(&DrmThread::RunTaskAfterWindowReady, base::Unretained(&drm_thread_), std::move(task)));
base::Unretained(&drm_thread_), widget, std::move(task)));
} }
void DrmThreadProxy::SetClearOverlayCacheCallback( void DrmThreadProxy::SetClearOverlayCacheCallback(
...@@ -162,9 +155,9 @@ void DrmThreadProxy::CheckOverlayCapabilities( ...@@ -162,9 +155,9 @@ void DrmThreadProxy::CheckOverlayCapabilities(
widget, candidates, CreateSafeOnceCallback(std::move(callback))); widget, candidates, CreateSafeOnceCallback(std::move(callback)));
drm_thread_.task_runner()->PostTask( drm_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady, FROM_HERE,
base::Unretained(&drm_thread_), widget, base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
std::move(task), nullptr)); base::Unretained(&drm_thread_), std::move(task), nullptr));
} }
std::vector<OverlayStatus> DrmThreadProxy::CheckOverlayCapabilitiesSync( std::vector<OverlayStatus> DrmThreadProxy::CheckOverlayCapabilitiesSync(
...@@ -177,10 +170,9 @@ std::vector<OverlayStatus> DrmThreadProxy::CheckOverlayCapabilitiesSync( ...@@ -177,10 +170,9 @@ std::vector<OverlayStatus> DrmThreadProxy::CheckOverlayCapabilitiesSync(
base::OnceClosure task = base::BindOnce( base::OnceClosure task = base::BindOnce(
&DrmThread::CheckOverlayCapabilitiesSync, base::Unretained(&drm_thread_), &DrmThread::CheckOverlayCapabilitiesSync, base::Unretained(&drm_thread_),
widget, candidates, &result); widget, candidates, &result);
PostSyncTask( PostSyncTask(drm_thread_.task_runner(),
drm_thread_.task_runner(), base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
base::BindOnce(&DrmThread::RunTaskAfterWindowReady, base::Unretained(&drm_thread_), std::move(task)));
base::Unretained(&drm_thread_), widget, std::move(task)));
return result; return result;
} }
......
...@@ -50,25 +50,24 @@ class DrmThreadTest : public testing::Test { ...@@ -50,25 +50,24 @@ class DrmThreadTest : public testing::Test {
drm_thread_.FlushForTesting(); drm_thread_.FlushForTesting();
} }
std::unique_ptr<base::WaitableEvent> PostStubTaskWithWaitableEvent( std::unique_ptr<base::WaitableEvent> PostStubTaskWithWaitableEvent() {
gfx::AcceleratedWidget window) {
base::OnceClosure task = base::BindOnce(StubTask); base::OnceClosure task = base::BindOnce(StubTask);
auto event = std::make_unique<base::WaitableEvent>( auto event = std::make_unique<base::WaitableEvent>(
base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent::InitialState::NOT_SIGNALED);
drm_thread_.task_runner()->PostTask( drm_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady, FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
base::Unretained(&drm_thread_), window, base::Unretained(&drm_thread_),
std::move(task), event.get())); std::move(task), event.get()));
return event; return event;
} }
void PostStubTask(gfx::AcceleratedWidget window, bool* done) { void PostStubTask(bool* done) {
*done = false; *done = false;
base::OnceClosure task = base::BindOnce(StubTaskWithDoneFeedback, done); base::OnceClosure task = base::BindOnce(StubTaskWithDoneFeedback, done);
drm_thread_.task_runner()->PostTask( drm_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady, FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
base::Unretained(&drm_thread_), window, base::Unretained(&drm_thread_),
std::move(task), nullptr)); std::move(task), nullptr));
} }
...@@ -84,87 +83,30 @@ class DrmThreadTest : public testing::Test { ...@@ -84,87 +83,30 @@ class DrmThreadTest : public testing::Test {
mojo::Remote<ozone::mojom::DrmDevice> drm_device_; mojo::Remote<ozone::mojom::DrmDevice> drm_device_;
}; };
TEST_F(DrmThreadTest, RunTaskAfterWindowReady) { TEST_F(DrmThreadTest, RunTaskAfterDeviceReady) {
constexpr gfx::Rect bounds(10, 10); bool called = false;
bool called1 = false, called2 = false;
gfx::AcceleratedWidget widget1 = 1, widget2 = 2;
// Post a task not blocked on any window. It should still block on a graphics // Post 2 tasks. One with WaitableEvent and one without. They should block on
// device becoming available. // a graphics device becoming available.
PostStubTask(gfx::kNullAcceleratedWidget, &called1); std::unique_ptr<base::WaitableEvent> event = PostStubTaskWithWaitableEvent();
PostStubTask(&called);
drm_thread_.FlushForTesting(); drm_thread_.FlushForTesting();
EXPECT_FALSE(called1); EXPECT_FALSE(event->IsSignaled());
EXPECT_FALSE(called);
// Add the graphics device. The task should run. // Add the graphics device. The tasks should run.
AddGraphicsDevice(); AddGraphicsDevice();
drm_thread_.FlushForTesting(); drm_thread_.FlushForTesting();
ASSERT_TRUE(called1);
// Now that a graphics device is available, further tasks that don't block on
// any window should execute immediately.
PostStubTask(gfx::kNullAcceleratedWidget, &called1);
drm_thread_.FlushForTesting();
ASSERT_TRUE(called1);
// Post a task blocked on |widget1|. It shouldn't run.
PostStubTask(widget1, &called1);
drm_thread_.FlushForTesting();
ASSERT_FALSE(called1);
// Post two tasks blocked on |widget2|, one with a WaitableEvent and one
// without. They shouldn't run.
std::unique_ptr<base::WaitableEvent> event =
PostStubTaskWithWaitableEvent(widget2);
PostStubTask(widget2, &called2);
drm_thread_.FlushForTesting();
ASSERT_FALSE(event->IsSignaled());
ASSERT_FALSE(called2);
// Now create |widget1|. The first task should run.
drm_device_->CreateWindow(widget1, bounds);
drm_thread_.FlushForTesting();
ASSERT_TRUE(called1);
ASSERT_FALSE(event->IsSignaled());
ASSERT_FALSE(called2);
// Now that |widget1| is created. any further task depending on it should run
// immediately.
PostStubTask(widget1, &called1);
drm_thread_.FlushForTesting();
ASSERT_TRUE(called1);
ASSERT_FALSE(event->IsSignaled());
ASSERT_FALSE(called2);
// Destroy |widget1| and post a task blocked on it. The task should still run
// immediately even though the window is destroyed.
drm_device_->DestroyWindow(widget1);
PostStubTask(widget1, &called1);
drm_thread_.FlushForTesting();
ASSERT_TRUE(called1);
ASSERT_FALSE(event->IsSignaled());
ASSERT_FALSE(called2);
// Create |widget2|. The two blocked tasks should run.
drm_device_->CreateWindow(widget2, bounds);
drm_thread_.FlushForTesting();
ASSERT_TRUE(event->IsSignaled()); ASSERT_TRUE(event->IsSignaled());
ASSERT_TRUE(called2); ASSERT_TRUE(called);
// Post another task blocked on |widget1| with a WaitableEvent. It should run // Now that a graphics device is available, further tasks should execute
// immediately. // immediately.
event = PostStubTaskWithWaitableEvent(widget1); event = PostStubTaskWithWaitableEvent();
PostStubTask(&called);
drm_thread_.FlushForTesting(); drm_thread_.FlushForTesting();
ASSERT_TRUE(event->IsSignaled()); ASSERT_TRUE(event->IsSignaled());
ASSERT_TRUE(called);
// Post another task blocked on |widget2| with a WaitableEvent. It should run
// immediately.
event = PostStubTaskWithWaitableEvent(widget2);
drm_thread_.FlushForTesting();
ASSERT_TRUE(event->IsSignaled());
// Destroy |widget2| to avoid failures during tear down.
drm_device_->DestroyWindow(widget2);
drm_thread_.FlushForTesting();
} }
// Verifies that we gracefully handle the case where CheckOverlayCapabilities() // Verifies that we gracefully handle the case where CheckOverlayCapabilities()
......
...@@ -35,9 +35,9 @@ void DrmWindowProxy::SchedulePageFlip( ...@@ -35,9 +35,9 @@ void DrmWindowProxy::SchedulePageFlip(
std::move(planes), CreateSafeOnceCallback(std::move(submission_callback)), std::move(planes), CreateSafeOnceCallback(std::move(submission_callback)),
CreateSafeOnceCallback(std::move(presentation_callback))); CreateSafeOnceCallback(std::move(presentation_callback)));
drm_thread_->task_runner()->PostTask( drm_thread_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DrmThread::RunTaskAfterWindowReady, FROM_HERE,
base::Unretained(drm_thread_), widget_, base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
std::move(task), nullptr)); base::Unretained(drm_thread_), std::move(task), nullptr));
} }
bool DrmWindowProxy::SupportsGpuFences() const { bool DrmWindowProxy::SupportsGpuFences() const {
...@@ -45,10 +45,9 @@ bool DrmWindowProxy::SupportsGpuFences() const { ...@@ -45,10 +45,9 @@ bool DrmWindowProxy::SupportsGpuFences() const {
base::OnceClosure task = base::OnceClosure task =
base::BindOnce(&DrmThread::IsDeviceAtomic, base::Unretained(drm_thread_), base::BindOnce(&DrmThread::IsDeviceAtomic, base::Unretained(drm_thread_),
widget_, &is_atomic); widget_, &is_atomic);
PostSyncTask( PostSyncTask(drm_thread_->task_runner(),
drm_thread_->task_runner(), base::BindOnce(&DrmThread::RunTaskAfterDeviceReady,
base::BindOnce(&DrmThread::RunTaskAfterWindowReady, base::Unretained(drm_thread_), std::move(task)));
base::Unretained(drm_thread_), widget_, std::move(task)));
return is_atomic && !base::CommandLine::ForCurrentProcess()->HasSwitch( return is_atomic && !base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableExplicitDmaFences); switches::kDisableExplicitDmaFences);
} }
......
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