Skip to content

Commit

Permalink
[FancyZones] Fix deadlocks in ZoneWindowDrawing (#10461)
Browse files Browse the repository at this point in the history
* Fixed deadlocks in ZoneWindowDrawing

Moved all possibly reentrant or blocking calls to ShowWindow out of critical sections.

* Initialize bools

* Tune flashing visuals

* Address PR comments

* Use  = true; to initialize bools
* Remove tracing from GetAnimationAlpha
* Use member initialization when constructing AnimationInfo

* Refactor rendering

* Whitespace

* Hide window on render failure
  • Loading branch information
ivan100sic committed Mar 29, 2021
1 parent ccc380f commit 31fa947
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 63 deletions.
4 changes: 2 additions & 2 deletions src/modules/fancyzones/lib/FancyZones.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ void FancyZones::ToggleEditor() noexcept
wil::unique_cotaskmem_string virtualDesktopId;
if (!SUCCEEDED(StringFromCLSID(m_currentDesktopId, &virtualDesktopId)))
{
return;
return;
}

/*
Expand Down Expand Up @@ -677,7 +677,7 @@ void FancyZones::ToggleEditor() noexcept
{
params += FancyZonesUtils::GenerateUniqueIdAllMonitorsArea(virtualDesktopId.get()) + divider; /* Monitor id where the Editor should be opened */
}

// device id map
std::unordered_map<std::wstring, DWORD> displayDeviceIdxMap;

Expand Down
6 changes: 2 additions & 4 deletions src/modules/fancyzones/lib/ZoneWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
std::vector<size_t> m_highlightZone;
WPARAM m_keyLast{};
size_t m_keyCycle{};
static const UINT m_showAnimationDuration = 200; // ms
static const UINT m_flashDuration = 1000; // ms
std::unique_ptr<ZoneWindowDrawing> m_zoneWindowDrawing;
};

Expand Down Expand Up @@ -384,7 +382,7 @@ ZoneWindow::ShowZoneWindow() noexcept
{
SetAsTopmostWindow();
m_zoneWindowDrawing->DrawActiveZoneSet(m_activeZoneSet->GetZones(), m_highlightZone, m_host);
m_zoneWindowDrawing->Show(m_showAnimationDuration);
m_zoneWindowDrawing->Show();
}
}

Expand Down Expand Up @@ -428,7 +426,7 @@ ZoneWindow::FlashZones() noexcept
{
SetAsTopmostWindow();
m_zoneWindowDrawing->DrawActiveZoneSet(m_activeZoneSet->GetZones(), {}, m_host);
m_zoneWindowDrawing->Flash(m_flashDuration);
m_zoneWindowDrawing->Flash();
}
}

Expand Down
113 changes: 61 additions & 52 deletions src/modules/fancyzones/lib/ZoneWindowDrawing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,36 @@

#include <common/logger/logger.h>

namespace
{
const int FadeInDurationMillis = 200;
const int FlashZonesDurationMillis = 700;
}

namespace NonLocalizable
{
const wchar_t SegoeUiFont[] = L"Segoe ui";
}

float ZoneWindowDrawing::GetAnimationAlpha()
{
// Lock is being held
// Lock is held by the caller

if (!m_animation)
{
return 0.f;
}

auto tNow = std::chrono::steady_clock().now();
auto alpha = (tNow - m_animation->tStart).count() / (1e6f * m_animation->duration);
auto millis = (tNow - m_animation->tStart).count() / 1e6f;

if (m_animation->fadeIn)
{
// Return a positive value to avoid hiding
return std::clamp(alpha, 0.001f, 1.f);
}
else
if (m_animation->autoHide && millis > FlashZonesDurationMillis)
{
// Quadratic function looks better
return std::clamp(1.f - alpha * alpha, 0.f, 1.f);
return 0.f;
}

// Return a positive value to avoid hiding
return std::clamp(millis / FadeInDurationMillis, 0.001f, 1.f);
}

ID2D1Factory* ZoneWindowDrawing::GetD2DFactory()
Expand Down Expand Up @@ -91,7 +95,7 @@ ZoneWindowDrawing::ZoneWindowDrawing(HWND window)
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
96.f,
96.f);

auto renderTargetSize = D2D1::SizeU(m_clientRect.right - m_clientRect.left, m_clientRect.bottom - m_clientRect.top);
auto hwndRenderTargetProperties = D2D1::HwndRenderTargetProperties(window, renderTargetSize);

Expand All @@ -106,20 +110,20 @@ ZoneWindowDrawing::ZoneWindowDrawing(HWND window)
m_renderThread = std::thread([this]() { RenderLoop(); });
}

void ZoneWindowDrawing::Render()
ZoneWindowDrawing::RenderResult ZoneWindowDrawing::Render()
{
std::unique_lock lock(m_mutex);

if (!m_renderTarget)
{
return;
return RenderResult::Failed;
}

float animationAlpha = GetAnimationAlpha();

if (animationAlpha <= 0.f)
{
return;
return RenderResult::AnimationEnded;
}

m_renderTarget->BeginDraw();
Expand Down Expand Up @@ -186,91 +190,96 @@ void ZoneWindowDrawing::Render()
lock.unlock();

m_renderTarget->EndDraw();
return RenderResult::Ok;
}

void ZoneWindowDrawing::RenderLoop()
{
while (!m_abortThread)
{
float animationAlpha;
{
// The lock must be held by the caller when calling GetAnimationAlpha
// Wait here while rendering is disabled
std::unique_lock lock(m_mutex);
animationAlpha = GetAnimationAlpha();
m_cv.wait(lock, [this]() { return (bool)m_shouldRender; });
}

// Check whether the animation expired and we need to hide the window
if (animationAlpha <= 0.f)
{
Hide();
}
auto result = Render();

if (result == RenderResult::AnimationEnded || result == RenderResult::Failed)
{
// Wait here while rendering is disabled
std::unique_lock lock(m_mutex);
m_cv.wait(lock, [this]() { return (bool)m_shouldRender; });
Hide();
}

Render();
}
}

void ZoneWindowDrawing::Hide()
{
_TRACER_;
std::unique_lock lock(m_mutex);

m_animation.reset();

if (m_shouldRender)
bool shouldHideWindow = true;
{
std::unique_lock lock(m_mutex);
m_animation.reset();
shouldHideWindow = m_shouldRender;
m_shouldRender = false;
}

if (shouldHideWindow)
{
ShowWindow(m_window, SW_HIDE);
}
}

void ZoneWindowDrawing::Show(unsigned animationMillis)
void ZoneWindowDrawing::Show()
{
_TRACER_;
std::unique_lock lock(m_mutex);

if (!m_shouldRender)
bool shouldShowWindow = true;
{
ShowWindow(m_window, SW_SHOWNA);
}
std::unique_lock lock(m_mutex);
shouldShowWindow = !m_shouldRender;
m_shouldRender = true;

animationMillis = max(animationMillis, 1);
if (!m_animation)
{
m_animation.emplace(AnimationInfo{ .tStart = std::chrono::steady_clock().now(), .autoHide = false });
}
else if (m_animation->autoHide)
{
// Do not change the starting time of the animation, just reset autoHide
m_animation->autoHide = false;
}
}

if (!m_animation || !m_animation->fadeIn)
if (shouldShowWindow)
{
m_animation.emplace(AnimationInfo{ std::chrono::steady_clock().now(), animationMillis, true });
ShowWindow(m_window, SW_SHOWNA);
}

m_shouldRender = true;
m_cv.notify_all();
}

void ZoneWindowDrawing::Flash(unsigned animationMillis)
void ZoneWindowDrawing::Flash()
{
_TRACER_;
std::unique_lock lock(m_mutex);
bool shouldShowWindow = true;
{
std::unique_lock lock(m_mutex);
shouldShowWindow = !m_shouldRender;
m_shouldRender = true;

m_animation.emplace(AnimationInfo{ .tStart = std::chrono::steady_clock().now(), .autoHide = true });
}

if (!m_shouldRender)
if (shouldShowWindow)
{
ShowWindow(m_window, SW_SHOWNA);
}

animationMillis = max(animationMillis, 1);

m_animation.emplace(AnimationInfo{ std::chrono::steady_clock().now(), animationMillis, false });

m_shouldRender = true;
m_cv.notify_all();
}

void ZoneWindowDrawing::DrawActiveZoneSet(const IZoneSet::ZonesMap& zones,
const std::vector<size_t>& highlightZones,
winrt::com_ptr<IZoneWindowHost> host)
const std::vector<size_t>& highlightZones,
winrt::com_ptr<IZoneWindowHost> host)
{
_TRACER_;
std::unique_lock lock(m_mutex);
Expand Down
16 changes: 11 additions & 5 deletions src/modules/fancyzones/lib/ZoneWindowDrawing.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ class ZoneWindowDrawing
struct AnimationInfo
{
std::chrono::steady_clock::time_point tStart;
unsigned duration;
bool fadeIn;
bool autoHide;
};

enum struct RenderResult
{
Ok,
AnimationEnded,
Failed,
};

HWND m_window = nullptr;
Expand All @@ -42,7 +48,7 @@ class ZoneWindowDrawing
static IDWriteFactory* GetWriteFactory();
static D2D1_COLOR_F ConvertColor(COLORREF color);
static D2D1_RECT_F ConvertRect(RECT rect);
void Render();
RenderResult Render();
void RenderLoop();

std::atomic<bool> m_shouldRender = false;
Expand All @@ -55,8 +61,8 @@ class ZoneWindowDrawing
~ZoneWindowDrawing();
ZoneWindowDrawing(HWND window);
void Hide();
void Show(unsigned animationMillis);
void Flash(unsigned animationMillis);
void Show();
void Flash();
void DrawActiveZoneSet(const IZoneSet::ZonesMap& zones,
const std::vector<size_t>& highlightZones,
winrt::com_ptr<IZoneWindowHost> host);
Expand Down

0 comments on commit 31fa947

Please sign in to comment.