Files
platform-external-webrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc
Austin Orion 78c73477c7 Add DesktopCaptureOption enumerate_current_process_windows to avoid hang
Enumerating windows owned by the current process on Windows has some
complications due to the GetWindowText*() APIs potentially causing a
deadlock. The APIs will send messages to the window's message loop, and
if the message loop is waiting on this operation we will enter a
deadlock.

I previously put in a mitigation for this [1] which brought the
incidence rate down by an order of magnitude, but we are still seeing
this issue fairly frequently.

So, I've added  DesktopCaptureOption enumerate_current_process_windows
which allows consumers to avoid this issue completely by ignoring
these potentially problematic windows.

By default the flag is set to true which equates with the current
behavior, consumers can set the flag to false to get the new behavior.

I've also updated all the capturers that enumerate windows on Windows
to respect the option.

[1] https://webrtc-review.googlesource.com/c/src/+/195365

Bug: chromium:1152841
Change-Id: I0e0d868957d6fbe1e607a440b3a909d005c93ccf
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/219380
Commit-Queue: Austin Orion <auorion@microsoft.com>
Reviewed-by: Joe Downing <joedow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#34191}
2021-06-01 18:20:50 +00:00

155 lines
5.6 KiB
C++

/*
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/desktop_capture/win/window_capture_utils.h"
#include <winuser.h>
#include <algorithm>
#include <memory>
#include <mutex>
#include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/win/test_support/test_window.h"
#include "rtc_base/thread.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
const char kWindowThreadName[] = "window_capture_utils_test_thread";
const WCHAR kWindowTitle[] = L"Window Capture Utils Test";
std::unique_ptr<rtc::Thread> SetUpUnresponsiveWindow(std::mutex& mtx,
WindowInfo& info) {
std::unique_ptr<rtc::Thread> window_thread;
window_thread = rtc::Thread::Create();
window_thread->SetName(kWindowThreadName, nullptr);
window_thread->Start();
window_thread->Invoke<void>(
RTC_FROM_HERE, [&info]() { info = CreateTestWindow(kWindowTitle); });
// Intentionally create a deadlock to cause the window to become unresponsive.
mtx.lock();
window_thread->PostTask(RTC_FROM_HERE, [&mtx]() {
mtx.lock();
mtx.unlock();
});
return window_thread;
}
} // namespace
TEST(WindowCaptureUtilsTest, GetWindowList) {
WindowInfo info = CreateTestWindow(kWindowTitle);
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
EXPECT_GT(window_list.size(), 0ULL);
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
DestroyTestWindow(info);
}
TEST(WindowCaptureUtilsTest, IncludeUnresponsiveWindows) {
std::mutex mtx;
WindowInfo info;
std::unique_ptr<rtc::Thread> window_thread =
SetUpUnresponsiveWindow(mtx, info);
EXPECT_FALSE(IsWindowResponding(info.hwnd));
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
EXPECT_GT(window_list.size(), 0ULL);
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
mtx.unlock();
window_thread->Invoke<void>(RTC_FROM_HERE,
[&info]() { DestroyTestWindow(info); });
window_thread->Stop();
}
TEST(WindowCaptureUtilsTest, IgnoreUnresponsiveWindows) {
std::mutex mtx;
WindowInfo info;
std::unique_ptr<rtc::Thread> window_thread =
SetUpUnresponsiveWindow(mtx, info);
EXPECT_FALSE(IsWindowResponding(info.hwnd));
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(
GetWindowList(GetWindowListFlags::kIgnoreUnresponsive, &window_list));
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
mtx.unlock();
window_thread->Invoke<void>(RTC_FROM_HERE,
[&info]() { DestroyTestWindow(info); });
window_thread->Stop();
}
TEST(WindowCaptureUtilsTest, IncludeUntitledWindows) {
WindowInfo info = CreateTestWindow(L"");
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
EXPECT_GT(window_list.size(), 0ULL);
EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
DestroyTestWindow(info);
}
TEST(WindowCaptureUtilsTest, IgnoreUntitledWindows) {
WindowInfo info = CreateTestWindow(L"");
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreUntitled, &window_list));
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
DestroyTestWindow(info);
}
TEST(WindowCaptureUtilsTest, IgnoreCurrentProcessWindows) {
WindowInfo info = CreateTestWindow(kWindowTitle);
DesktopCapturer::SourceList window_list;
ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreCurrentProcessWindows,
&window_list));
EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
[&info](DesktopCapturer::Source window) {
return reinterpret_cast<HWND>(window.id) ==
info.hwnd;
}),
window_list.end());
DestroyTestWindow(info);
}
} // namespace webrtc