QCalendar: Delete registered calendar backends on program exit

Add code to check if the calendar registry is destroyed to all
QCalendar methods that dereference QCalendarBackend pointer.

Pick-to: 6.2
Change-Id: I9b562355e2e0579396b52968f6065c6927cc9ca8
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Ievgenii Meshcheriakov 2021-07-22 17:44:36 +02:00
parent d0ae1ef33a
commit 84a93d94ac
2 changed files with 76 additions and 17 deletions

View File

@ -118,7 +118,12 @@ class QCalendarRegistry
*/ */
QAtomicPointer<const QCalendarBackend> gregorianCalendar = nullptr; QAtomicPointer<const QCalendarBackend> gregorianCalendar = nullptr;
enum : int { Unpopulated, Populated }; enum : int {
Unpopulated, // The standard backends may not yet be created
Populated, // All standard backends were created
IsBeingDestroyed, // The registry and the backends are being destroyed
};
/* /*
Fast way to check whether the standard calendars were populated. Fast way to check whether the standard calendars were populated.
@ -134,6 +139,10 @@ class QCalendarRegistry
public: public:
QCalendarRegistry() { byId.resize(int(QCalendar::System::Last) + 1); } QCalendarRegistry() { byId.resize(int(QCalendar::System::Last) + 1); }
~QCalendarRegistry();
bool isBeingDestroyed() const { return status.loadRelaxed() == IsBeingDestroyed; }
void registerCustomBackend(QCalendarBackend *backend, const QStringList &names); void registerCustomBackend(QCalendarBackend *backend, const QStringList &names);
QStringList availableCalendars(); QStringList availableCalendars();
@ -168,6 +177,21 @@ public:
QStringList backendNames(const QCalendarBackend *backend); QStringList backendNames(const QCalendarBackend *backend);
}; };
/*!
Destroy the registry.
This destroys all registered backends. This destructor should only be called
in a single-threaded context at program exit.
*/
QCalendarRegistry::~QCalendarRegistry()
{
QWriteLocker locker(&lock);
status.storeRelaxed(IsBeingDestroyed);
qDeleteAll(byId);
}
/*! /*!
Registers a custom backend. Registers a custom backend.
@ -508,7 +532,7 @@ Q_GLOBAL_STATIC(QtPrivate::QCalendarRegistry, calendarRegistry);
Destroys the calendar backend. Destroys the calendar backend.
Each calendar backend, once instantiated and successfully registered by ID, Each calendar backend, once instantiated and successfully registered by ID,
shall exist for the lifetime of the program. Destroying a shall exist until it is destroyed by the registry. Destroying a
successfully-registered backend otherwise may leave existing QCalendar successfully-registered backend otherwise may leave existing QCalendar
instances referencing the destroyed calendar, with undefined results. instances referencing the destroyed calendar, with undefined results.
@ -518,7 +542,8 @@ Q_GLOBAL_STATIC(QtPrivate::QCalendarRegistry, calendarRegistry);
*/ */
QCalendarBackend::~QCalendarBackend() QCalendarBackend::~QCalendarBackend()
{ {
Q_ASSERT(!m_id.isValid()); Q_ASSERT(!m_id.isValid() || calendarRegistry.isDestroyed()
|| calendarRegistry->isBeingDestroyed());
} }
/*! /*!
@ -616,6 +641,15 @@ QCalendar::System QCalendarBackend::calendarSystem() const
return m_id.isInEnum() ? QCalendar::System(m_id.index()) : QCalendar::System::User; return m_id.isInEnum() ? QCalendar::System(m_id.index()) : QCalendar::System::User;
} }
/*
Create local variable d containing the backend associated with a QCalendar
instance unless the calendar registry is destroyed together with all backends,
then return nullptr.
This assumes that the registry is only destroyed in single threaded context.
*/
#define SAFE_D() const auto d = Q_UNLIKELY(calendarRegistry.isDestroyed()) ? nullptr : d_ptr
/*! /*!
The primary name of this calendar. The primary name of this calendar.
@ -625,6 +659,7 @@ QCalendar::System QCalendarBackend::calendarSystem() const
*/ */
QString QCalendar::name() const QString QCalendar::name() const
{ {
SAFE_D();
return d ? d->name() : QString(); return d ? d->name() : QString();
} }
@ -1141,17 +1176,16 @@ const QCalendarBackend *QCalendarBackend::gregorian()
*/ */
QCalendar::QCalendar() QCalendar::QCalendar()
: d(QCalendarBackend::gregorian()) : d_ptr(QCalendarBackend::gregorian())
{ {
Q_ASSERT(!d || d->calendarId().isValid()); Q_ASSERT(!d_ptr || d_ptr->calendarId().isValid());
} }
QCalendar::QCalendar(QCalendar::System system) QCalendar::QCalendar(QCalendar::System system) : d_ptr(QCalendarBackend::fromEnum(system))
: d(QCalendarBackend::fromEnum(system))
{ {
// If system is valid, we should get a valid d for that system. // If system is valid, we should get a valid d for that system.
Q_ASSERT(!d || (uint(system) > uint(QCalendar::System::Last)) Q_ASSERT(!d_ptr || (uint(system) > uint(QCalendar::System::Last))
|| (d->calendarId().index() == size_t(system))); || (d_ptr->calendarId().index() == size_t(system)));
} }
/*! /*!
@ -1166,21 +1200,21 @@ QCalendar::QCalendar(QCalendar::System system)
registered by name. registered by name.
*/ */
QCalendar::QCalendar(QCalendar::SystemId id) QCalendar::QCalendar(QCalendar::SystemId id)
: d(QCalendarBackend::fromId(id)) : d_ptr(QCalendarBackend::fromId(id))
{ {
Q_ASSERT(!d || d->calendarId().index() == id.index()); Q_ASSERT(!d_ptr || d_ptr->calendarId().index() == id.index());
} }
QCalendar::QCalendar(QLatin1String name) QCalendar::QCalendar(QLatin1String name)
: d(QCalendarBackend::fromName(name)) : d_ptr(QCalendarBackend::fromName(name))
{ {
Q_ASSERT(!d || d->calendarId().isValid()); Q_ASSERT(!d_ptr || d_ptr->calendarId().isValid());
} }
QCalendar::QCalendar(QStringView name) QCalendar::QCalendar(QStringView name)
: d(QCalendarBackend::fromName(name)) : d_ptr(QCalendarBackend::fromName(name))
{ {
Q_ASSERT(!d || d->calendarId().isValid()); Q_ASSERT(!d_ptr || d_ptr->calendarId().isValid());
} }
/*! /*!
@ -1205,6 +1239,7 @@ QCalendar::QCalendar(QStringView name)
*/ */
int QCalendar::daysInMonth(int month, int year) const int QCalendar::daysInMonth(int month, int year) const
{ {
SAFE_D();
return d ? d->daysInMonth(month, year) : 0; return d ? d->daysInMonth(month, year) : 0;
} }
@ -1215,6 +1250,7 @@ int QCalendar::daysInMonth(int month, int year) const
*/ */
int QCalendar::daysInYear(int year) const int QCalendar::daysInYear(int year) const
{ {
SAFE_D();
return d ? d->daysInYear(year) : 0; return d ? d->daysInYear(year) : 0;
} }
@ -1228,6 +1264,7 @@ int QCalendar::daysInYear(int year) const
*/ */
int QCalendar::monthsInYear(int year) const int QCalendar::monthsInYear(int year) const
{ {
SAFE_D();
return d ? year == Unspecified ? d->maximumMonthsInYear() : d->monthsInYear(year) : 0; return d ? year == Unspecified ? d->maximumMonthsInYear() : d->monthsInYear(year) : 0;
} }
@ -1241,6 +1278,7 @@ int QCalendar::monthsInYear(int year) const
*/ */
bool QCalendar::isDateValid(int year, int month, int day) const bool QCalendar::isDateValid(int year, int month, int day) const
{ {
SAFE_D();
return d && d->isDateValid(year, month, day); return d && d->isDateValid(year, month, day);
} }
@ -1252,6 +1290,7 @@ bool QCalendar::isDateValid(int year, int month, int day) const
*/ */
bool QCalendar::isGregorian() const bool QCalendar::isGregorian() const
{ {
SAFE_D();
return d && d->isGregorian(); return d && d->isGregorian();
} }
@ -1266,6 +1305,7 @@ bool QCalendar::isGregorian() const
*/ */
bool QCalendar::isLeapYear(int year) const bool QCalendar::isLeapYear(int year) const
{ {
SAFE_D();
return d && d->isLeapYear(year); return d && d->isLeapYear(year);
} }
@ -1276,6 +1316,7 @@ bool QCalendar::isLeapYear(int year) const
*/ */
bool QCalendar::isLunar() const bool QCalendar::isLunar() const
{ {
SAFE_D();
return d && d->isLunar(); return d && d->isLunar();
} }
@ -1288,6 +1329,7 @@ bool QCalendar::isLunar() const
*/ */
bool QCalendar::isLuniSolar() const bool QCalendar::isLuniSolar() const
{ {
SAFE_D();
return d && d->isLuniSolar(); return d && d->isLuniSolar();
} }
@ -1299,6 +1341,7 @@ bool QCalendar::isLuniSolar() const
*/ */
bool QCalendar::isSolar() const bool QCalendar::isSolar() const
{ {
SAFE_D();
return d && d->isSolar(); return d && d->isSolar();
} }
@ -1313,6 +1356,7 @@ bool QCalendar::isSolar() const
*/ */
bool QCalendar::isProleptic() const bool QCalendar::isProleptic() const
{ {
SAFE_D();
return d && d->isProleptic(); return d && d->isProleptic();
} }
@ -1343,6 +1387,7 @@ bool QCalendar::isProleptic() const
*/ */
bool QCalendar::hasYearZero() const bool QCalendar::hasYearZero() const
{ {
SAFE_D();
return d && d->hasYearZero(); return d && d->hasYearZero();
} }
@ -1353,6 +1398,7 @@ bool QCalendar::hasYearZero() const
*/ */
int QCalendar::maximumDaysInMonth() const int QCalendar::maximumDaysInMonth() const
{ {
SAFE_D();
return d ? d->maximumDaysInMonth() : 0; return d ? d->maximumDaysInMonth() : 0;
} }
@ -1363,6 +1409,7 @@ int QCalendar::maximumDaysInMonth() const
*/ */
int QCalendar::minimumDaysInMonth() const int QCalendar::minimumDaysInMonth() const
{ {
SAFE_D();
return d ? d->minimumDaysInMonth() : 0; return d ? d->minimumDaysInMonth() : 0;
} }
@ -1373,6 +1420,7 @@ int QCalendar::minimumDaysInMonth() const
*/ */
int QCalendar::maximumMonthsInYear() const int QCalendar::maximumMonthsInYear() const
{ {
SAFE_D();
return d ? d->maximumMonthsInYear() : 0; return d ? d->maximumMonthsInYear() : 0;
} }
@ -1394,6 +1442,7 @@ int QCalendar::maximumMonthsInYear() const
*/ */
QDate QCalendar::dateFromParts(int year, int month, int day) const QDate QCalendar::dateFromParts(int year, int month, int day) const
{ {
SAFE_D();
qint64 jd; qint64 jd;
return d && d->dateToJulianDay(year, month, day, &jd) return d && d->dateToJulianDay(year, month, day, &jd)
? QDate::fromJulianDay(jd) : QDate(); ? QDate::fromJulianDay(jd) : QDate();
@ -1415,6 +1464,7 @@ QDate QCalendar::dateFromParts(const QCalendar::YearMonthDay &parts) const
*/ */
QCalendar::YearMonthDay QCalendar::partsFromDate(QDate date) const QCalendar::YearMonthDay QCalendar::partsFromDate(QDate date) const
{ {
SAFE_D();
return d && date.isValid() ? d->julianDayToDate(date.toJulianDay()) : YearMonthDay(); return d && date.isValid() ? d->julianDayToDate(date.toJulianDay()) : YearMonthDay();
} }
@ -1429,6 +1479,7 @@ QCalendar::YearMonthDay QCalendar::partsFromDate(QDate date) const
*/ */
int QCalendar::dayOfWeek(QDate date) const int QCalendar::dayOfWeek(QDate date) const
{ {
SAFE_D();
return d && date.isValid() ? d->dayOfWeek(date.toJulianDay()) : 0; return d && date.isValid() ? d->dayOfWeek(date.toJulianDay()) : 0;
} }
@ -1456,6 +1507,7 @@ int QCalendar::dayOfWeek(QDate date) const
QString QCalendar::monthName(const QLocale &locale, int month, int year, QString QCalendar::monthName(const QLocale &locale, int month, int year,
QLocale::FormatType format) const QLocale::FormatType format) const
{ {
SAFE_D();
const int maxMonth = year == Unspecified ? maximumMonthsInYear() : monthsInYear(year); const int maxMonth = year == Unspecified ? maximumMonthsInYear() : monthsInYear(year);
if (!d || month < 1 || month > maxMonth) if (!d || month < 1 || month > maxMonth)
return QString(); return QString();
@ -1485,6 +1537,7 @@ QString QCalendar::monthName(const QLocale &locale, int month, int year,
QString QCalendar::standaloneMonthName(const QLocale &locale, int month, int year, QString QCalendar::standaloneMonthName(const QLocale &locale, int month, int year,
QLocale::FormatType format) const QLocale::FormatType format) const
{ {
SAFE_D();
const int maxMonth = year == Unspecified ? maximumMonthsInYear() : monthsInYear(year); const int maxMonth = year == Unspecified ? maximumMonthsInYear() : monthsInYear(year);
if (!d || month < 1 || month > maxMonth) if (!d || month < 1 || month > maxMonth)
return QString(); return QString();
@ -1509,6 +1562,7 @@ QString QCalendar::standaloneMonthName(const QLocale &locale, int month, int yea
QString QCalendar::weekDayName(const QLocale &locale, int day, QString QCalendar::weekDayName(const QLocale &locale, int day,
QLocale::FormatType format) const QLocale::FormatType format) const
{ {
SAFE_D();
return d ? d->weekDayName(locale, day, format) : QString(); return d ? d->weekDayName(locale, day, format) : QString();
} }
@ -1531,6 +1585,7 @@ QString QCalendar::weekDayName(const QLocale &locale, int day,
QString QCalendar::standaloneWeekDayName(const QLocale &locale, int day, QString QCalendar::standaloneWeekDayName(const QLocale &locale, int day,
QLocale::FormatType format) const QLocale::FormatType format) const
{ {
SAFE_D();
return d ? d->standaloneWeekDayName(locale, day, format) : QString(); return d ? d->standaloneWeekDayName(locale, day, format) : QString();
} }
@ -1557,6 +1612,7 @@ QString QCalendar::dateTimeToString(QStringView format, const QDateTime &datetim
QDate dateOnly, QTime timeOnly, QDate dateOnly, QTime timeOnly,
const QLocale &locale) const const QLocale &locale) const
{ {
SAFE_D();
return d ? d->dateTimeToString(format, datetime, dateOnly, timeOnly, locale) : QString(); return d ? d->dateTimeToString(format, datetime, dateOnly, timeOnly, locale) : QString();
} }

View File

@ -153,7 +153,7 @@ public:
explicit QCalendar(SystemId id); explicit QCalendar(SystemId id);
// QCalendar is a trivially copyable value type. // QCalendar is a trivially copyable value type.
bool isValid() const { return d != nullptr; } bool isValid() const { return d_ptr != nullptr; }
// Date queries: // Date queries:
int daysInMonth(int month, int year = Unspecified) const; int daysInMonth(int month, int year = Unspecified) const;
@ -201,7 +201,10 @@ public:
static QStringList availableCalendars(); static QStringList availableCalendars();
private: private:
// Always supplied by QCalendarBackend and expected to be a singleton // Always supplied by QCalendarBackend and expected to be a singleton
const QCalendarBackend *d; // Note that the calendar registry destroys all backends when it is itself
// destroyed. The code should check if the registry is destroyed before
// dereferencing this pointer.
const QCalendarBackend *d_ptr;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE