QTzTimeZonePrivate: fix permanently-detaching m_icu

The m_icu member is a mutable QSharedDataPointer, which means that
only the non-const API subset is accessible, and so any access to it
will always detach, in 5.15 even a check like if (m_icu), which the
code luckily doesn't use (Qt 6 added a operator bool() const).

We don't need detaching behavior here, though, since, once set, m_icu
is never changed. So just use a QExplicitlySharedDataPointer instead,
and never call detach() (which would do the wrong thing). Just in case
someone does add a detach() later, instantiate
QExplicitlySharedDataPointer over QIcuTimeZonePrivate directly. This
requires making displayName() overloads from QTimeZonePrivate visible
in QIcuTimeZonePrivate. Add an assertion that QIcuTimeZonePrivate is
final, with instructions on what to do if it fails. Finally, hold a
pointer-to-const to avoid race conditions in the pointee.

The code still contains a data race, due to the lazy initialization of
m_icu, but now we have at least a fighting change to fix it.

Pick-to: 6.3 6.2 5.15
Change-Id: I32c343822dac43f96d9fbc4c759fa44138861eae
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2022-01-04 16:55:03 +01:00
parent 2dea20e4b0
commit 23aa51991d

View File

@ -243,6 +243,7 @@ public:
QIcuTimeZonePrivate *clone() const override;
using QTimeZonePrivate::displayName;
QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType,
const QLocale &locale) const override;
QString abbreviation(qint64 atMSecsSinceEpoch) const override;
@ -353,7 +354,12 @@ private:
Data dataForTzTransition(QTzTransitionTime tran) const;
Data dataFromRule(QTzTransitionRule rule, qint64 msecsSinceEpoch) const;
#if QT_CONFIG(icu)
mutable QSharedDataPointer<QTimeZonePrivate> m_icu;
# ifdef __cpp_lib_is_final
static_assert(std::is_final<QIcuTimeZonePrivate>::value,
"if QIcuTimeZonePrivate isn't final, we may need to specialize "
"QExplicitlySharedDataPointer::clone() to call QTimeZonePrivate::clone()");
# endif
mutable QExplicitlySharedDataPointer<const QIcuTimeZonePrivate> m_icu;
#endif
QTzTimeZoneCacheEntry cached_data;
QList<QTzTransitionTime> tranCache() const { return cached_data.m_tranTimes; }