Fix Q_GLOBAL_STATIC support for exceptions
The problem was that the HolderBase destructor was getting called after the contained type's constructor threw an exception, as is required by RAII semantics (the base was fully initialized, so it had to be destroyed). That was required because we want to return a non-null pointer from operator() during destruction and return null after destruction, to keep compatibility with Qt 4. The solution is to only set the guard to Destroyed only if it is already at value Initialized. This way, if the HolderBase destructor is run as part of the stack unwinding, it knows that the construction did not complete. Change-Id: I9849b43ed7112bf9e70861b48a56a924c286617e Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
This commit is contained in:
parent
ebfd85a499
commit
50d3a2917e
@ -288,9 +288,11 @@
|
||||
structure so holder's destructor can set the guard variable to the value -2
|
||||
(destroyed) when the type has finished destruction. Since we need to set
|
||||
the guard \b after the destruction has finished, this code needs to be in a
|
||||
base struct's destructor. A holder structure is used to avoid creating two
|
||||
statics, which the ABI might require duplicating the thread-safe control
|
||||
structures for.
|
||||
base struct's destructor. And it only sets to -2 (destroyed) if it finds
|
||||
the guard at -1 (initialized): this is done to ensure that the guard isn't
|
||||
set to -2 in the event the type's constructor threw an exception. A holder
|
||||
structure is used to avoid creating two statics, which the ABI might
|
||||
require duplicating the thread-safe control structures for.
|
||||
|
||||
The other implementation is similar to Qt 4's Q_GLOBAL_STATIC, but unlike
|
||||
that one, it uses a \l QBasicMutex to provide locking. It is also more
|
||||
|
@ -68,7 +68,8 @@ enum GuardValues {
|
||||
{ \
|
||||
struct HolderBase { \
|
||||
~HolderBase() Q_DECL_NOTHROW \
|
||||
{ guard.store(QtGlobalStatic::Destroyed); } \
|
||||
{ if (guard.load() == QtGlobalStatic::Initialized) \
|
||||
guard.store(QtGlobalStatic::Destroyed); } \
|
||||
}; \
|
||||
static struct Holder : public HolderBase { \
|
||||
Type value; \
|
||||
|
@ -50,6 +50,7 @@ private Q_SLOTS:
|
||||
void api();
|
||||
void constVolatile();
|
||||
void exception();
|
||||
void threadedException();
|
||||
void threadStressTest();
|
||||
void afterDestruction();
|
||||
};
|
||||
@ -107,6 +108,11 @@ struct ThrowingType
|
||||
{
|
||||
static QBasicAtomicInt constructedCount;
|
||||
static QBasicAtomicInt destructedCount;
|
||||
ThrowingType()
|
||||
{
|
||||
throw 0;
|
||||
}
|
||||
|
||||
ThrowingType(QBasicAtomicInt &throwControl)
|
||||
{
|
||||
constructedCount.ref();
|
||||
@ -115,12 +121,28 @@ struct ThrowingType
|
||||
}
|
||||
~ThrowingType() { destructedCount.ref(); }
|
||||
};
|
||||
|
||||
QBasicAtomicInt ThrowingType::constructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||
QBasicAtomicInt ThrowingType::destructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||
|
||||
Q_GLOBAL_STATIC(ThrowingType, throwingGS)
|
||||
void tst_QGlobalStatic::exception()
|
||||
{
|
||||
bool exceptionCaught = false;
|
||||
try {
|
||||
throwingGS();
|
||||
} catch (int) {
|
||||
exceptionCaught = true;
|
||||
}
|
||||
QVERIFY(exceptionCaught);
|
||||
QCOMPARE(Q_QGS_throwingGS::guard.load(), 0);
|
||||
QVERIFY(!throwingGS.exists());
|
||||
QVERIFY(!throwingGS.isDestroyed());
|
||||
}
|
||||
|
||||
QBasicAtomicInt exceptionControlVar = Q_BASIC_ATOMIC_INITIALIZER(1);
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, exceptionGS, (exceptionControlVar))
|
||||
void tst_QGlobalStatic::exception()
|
||||
void tst_QGlobalStatic::threadedException()
|
||||
{
|
||||
if (exceptionControlVar.load() != 1)
|
||||
QSKIP("This test cannot be run more than once");
|
||||
|
Loading…
Reference in New Issue
Block a user