Proper clearing of WinRT factory cache

If we use winrt's factories we have to make sure to to clear the factory
cache when one of our dlls is unloaded or we will run into dangling
factory entries which might result in crashes. So we have to make sure
that winrt::clear_factory_cache is called on every dll unload.

In order not to increase compile times and dependencies too much
qfactorycacheregistration_p.h needs to be included in Qt code whenever
we use winrt's factory cache. A rule of thumb being: Include
qfactorycacheregistration_p.h whenever including winrt/base.h.

Other Qt modules which use winrt's factories need to be updated too.

Fixes: QTBUG-103611
Change-Id: I7ab24e4b18bffaca653c5b7f56a66ce99212e339
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit ffb9dee1b0954e4d4f9e9791175609a80ecafc31)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Andreas Buhr 2022-06-15 16:14:47 +02:00 committed by Qt Cherry-pick Bot
parent ebd259fbbe
commit 7b7c9c6996
9 changed files with 136 additions and 2 deletions

View File

@ -973,6 +973,12 @@ qt_internal_extend_target(Core CONDITION ANDROID
platform/android/qandroidnativeinterface.cpp platform/android/qandroidnativeinterface.cpp
) )
qt_internal_extend_target(Core CONDITION WIN32
SOURCES
platform/windows/qfactorycacheregistration_p.h
platform/windows/qfactorycacheregistration.cpp
)
qt_internal_extend_target(Core CONDITION HAIKU AND NOT ANDROID qt_internal_extend_target(Core CONDITION HAIKU AND NOT ANDROID
SOURCES SOURCES
io/qstandardpaths_haiku.cpp io/qstandardpaths_haiku.cpp

View File

@ -743,7 +743,7 @@ qt_feature("xmlstream" PUBLIC
LABEL "XML Streaming APIs" LABEL "XML Streaming APIs"
PURPOSE "Provides a simple streaming API for XML." PURPOSE "Provides a simple streaming API for XML."
) )
qt_feature("cpp-winrt" PRIVATE qt_feature("cpp-winrt" PRIVATE PUBLIC
LABEL "cpp/winrt base" LABEL "cpp/winrt base"
PURPOSE "basic cpp/winrt language projection support" PURPOSE "basic cpp/winrt language projection support"
CONDITION WIN32 AND TEST_cpp_winrt CONDITION WIN32 AND TEST_cpp_winrt

View File

@ -0,0 +1,53 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qfactorycacheregistration_p.h"
#include <QtCore/QMutex>
QT_BEGIN_NAMESPACE
#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
static QBasicMutex registrationMutex;
static detail::QWinRTFactoryCacheRegistration *firstElement;
detail::QWinRTFactoryCacheRegistration::QWinRTFactoryCacheRegistration(
QFunctionPointer clearFunction)
: m_clearFunction(clearFunction)
{
QMutexLocker lock(&registrationMutex);
// forward pointers
m_next = std::exchange(firstElement, this);
// backward pointers
m_prevNext = &firstElement;
if (m_next)
m_next->m_prevNext = &m_next;
}
detail::QWinRTFactoryCacheRegistration::~QWinRTFactoryCacheRegistration()
{
QMutexLocker lock(&registrationMutex);
*m_prevNext = m_next;
if (m_next)
m_next->m_prevNext = m_prevNext;
}
void detail::QWinRTFactoryCacheRegistration::clearAllCaches()
{
QMutexLocker lock(&registrationMutex);
detail::QWinRTFactoryCacheRegistration *element;
for (element = firstElement; element != nullptr; element = element->m_next) {
element->m_clearFunction();
}
}
#endif
QT_END_NAMESPACE

View File

@ -0,0 +1,52 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFACTORYCACHEREGISTRATION_P_H
#define QFACTORYCACHEREGISTRATION_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qglobal.h>
#if !defined(QT_BOOTSTRAPPED) && defined(Q_OS_WIN) && !defined(Q_CC_CLANG) && QT_CONFIG(cpp_winrt)
# define QT_USE_FACTORY_CACHE_REGISTRATION
#endif
#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
#include <winrt/base.h>
QT_BEGIN_NAMESPACE
namespace detail {
class QWinRTFactoryCacheRegistration
{
public:
Q_CORE_EXPORT explicit QWinRTFactoryCacheRegistration(QFunctionPointer clearFunction);
Q_CORE_EXPORT ~QWinRTFactoryCacheRegistration();
Q_CORE_EXPORT static void clearAllCaches();
Q_DISABLE_COPY_MOVE(QWinRTFactoryCacheRegistration)
private:
QWinRTFactoryCacheRegistration **m_prevNext = nullptr;
QWinRTFactoryCacheRegistration *m_next = nullptr;
QFunctionPointer m_clearFunction;
};
inline QWinRTFactoryCacheRegistration reg([]() noexcept { winrt::clear_factory_cache(); });
}
QT_END_NAMESPACE
#endif
#endif // QFACTORYCACHEREGISTRATION_P_H

View File

@ -19,6 +19,7 @@
#if QT_CONFIG(cpp_winrt) && !defined(Q_CC_CLANG) #if QT_CONFIG(cpp_winrt) && !defined(Q_CC_CLANG)
# include <winrt/base.h> # include <winrt/base.h>
# include <QtCore/private/qfactorycacheregistration_p.h>
// Workaround for Windows SDK bug. // Workaround for Windows SDK bug.
// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47 // See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47
namespace winrt::impl namespace winrt::impl

View File

@ -5,6 +5,7 @@
#ifdef SUPPORTS_WINRT #ifdef SUPPORTS_WINRT
#include <winrt/base.h> #include <winrt/base.h>
#include <QtCore/private/qfactorycacheregistration_p.h>
// Workaround for Windows SDK bug. // Workaround for Windows SDK bug.
// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47 // See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47
namespace winrt::impl namespace winrt::impl

View File

@ -21,6 +21,7 @@
#ifdef SUPPORTS_WINRT #ifdef SUPPORTS_WINRT
#include <winrt/base.h> #include <winrt/base.h>
#include <QtCore/private/qfactorycacheregistration_p.h>
#endif #endif
using namespace Microsoft::WRL; using namespace Microsoft::WRL;

View File

@ -45,6 +45,7 @@
#include <QtCore/qscopedpointer.h> #include <QtCore/qscopedpointer.h>
#include <QtCore/quuid.h> #include <QtCore/quuid.h>
#include <QtCore/private/qwinregistry_p.h> #include <QtCore/private/qwinregistry_p.h>
#include <QtCore/private/qfactorycacheregistration_p.h>
#include <QtGui/private/qwindowsguieventdispatcher_p.h> #include <QtGui/private/qwindowsguieventdispatcher_p.h>
@ -221,8 +222,12 @@ QWindowsContext::~QWindowsContext()
DestroyWindow(d->m_powerDummyWindow); DestroyWindow(d->m_powerDummyWindow);
unregisterWindowClasses(); unregisterWindowClasses();
if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) {
#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
detail::QWinRTFactoryCacheRegistration::clearAllCaches();
#endif
OleUninitialize(); OleUninitialize();
}
d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows. d->m_screenManager.clearScreens(); // Order: Potentially calls back to the windows.
if (d->m_displayContext) if (d->m_displayContext)

View File

@ -128,6 +128,7 @@ private slots:
void wheelEventPropagation(); void wheelEventPropagation();
void qtbug_12673(); void qtbug_12673();
void qtbug_103611();
void noQuitOnHide(); void noQuitOnHide();
void globalStaticObjectDestruction(); // run this last void globalStaticObjectDestruction(); // run this last
@ -2513,6 +2514,20 @@ void tst_QApplication::qtbug_12673()
#endif #endif
} }
void tst_QApplication::qtbug_103611()
{
{
int argc = 0;
QApplication app(argc, nullptr);
auto ll = QLocale().uiLanguages();
}
{
int argc = 0;
QApplication app(argc, nullptr);
auto ll = QLocale().uiLanguages();
}
}
class NoQuitOnHideWidget : public QWidget class NoQuitOnHideWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT