summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/global.pri1
-rw-r--r--src/corelib/global/qglobal.h122
-rw-r--r--src/corelib/global/qglobalstatic.h157
-rw-r--r--tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro12
-rw-r--r--tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp214
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp10
6 files changed, 397 insertions, 119 deletions
diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri
index 01756c8419..491464621c 100644
--- a/src/corelib/global/global.pri
+++ b/src/corelib/global/global.pri
@@ -9,6 +9,7 @@ HEADERS += \
global/qendian.h \
global/qnumeric_p.h \
global/qnumeric.h \
+ global/qglobalstatic.h \
global/qlibraryinfo.h \
global/qlogging.h \
global/qtypeinfo.h \
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 03cd3dc591..60b6e32522 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -664,125 +664,6 @@ typedef void (*QFunctionPointer)();
# define Q_UNIMPLEMENTED() qWarning("%s:%d: %s: Unimplemented code.", __FILE__, __LINE__, Q_FUNC_INFO)
#endif
-#if defined(QT_NO_THREAD)
-
-template <typename T>
-class QGlobalStatic
-{
-public:
- T *pointer;
- inline QGlobalStatic(T *p) : pointer(p) { }
- inline ~QGlobalStatic() { pointer = 0; }
-};
-
-#define Q_GLOBAL_STATIC(TYPE, NAME) \
- static TYPE *NAME() \
- { \
- static TYPE thisVariable; \
- static QGlobalStatic<TYPE > thisGlobalStatic(&thisVariable); \
- return thisGlobalStatic.pointer; \
- }
-
-#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
- static TYPE *NAME() \
- { \
- static TYPE thisVariable ARGS; \
- static QGlobalStatic<TYPE > thisGlobalStatic(&thisVariable); \
- return thisGlobalStatic.pointer; \
- }
-
-#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \
- static TYPE *NAME() \
- { \
- static TYPE thisVariable; \
- static QGlobalStatic<TYPE > thisGlobalStatic(0); \
- if (!thisGlobalStatic.pointer) { \
- TYPE *x = thisGlobalStatic.pointer = &thisVariable; \
- INITIALIZER; \
- } \
- return thisGlobalStatic.pointer; \
- }
-
-#else
-
-// forward declaration, since qatomic.h needs qglobal.h
-template <typename T> class QBasicAtomicPointer;
-
-// POD for Q_GLOBAL_STATIC
-template <typename T>
-class QGlobalStatic
-{
-public:
- QBasicAtomicPointer<T> pointer;
- bool destroyed;
-};
-
-// Created as a function-local static to delete a QGlobalStatic<T>
-template <typename T>
-class QGlobalStaticDeleter
-{
-public:
- QGlobalStatic<T> &globalStatic;
- QGlobalStaticDeleter(QGlobalStatic<T> &_globalStatic)
- : globalStatic(_globalStatic)
- { }
-
- inline ~QGlobalStaticDeleter()
- {
- delete globalStatic.pointer.load();
- globalStatic.pointer.store(0);
- globalStatic.destroyed = true;
- }
-};
-
-#define Q_GLOBAL_STATIC(TYPE, NAME) \
- static TYPE *NAME() \
- { \
- static QGlobalStatic<TYPE > thisGlobalStatic \
- = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \
- if (!thisGlobalStatic.pointer.load() && !thisGlobalStatic.destroyed) { \
- TYPE *x = new TYPE; \
- if (!thisGlobalStatic.pointer.testAndSetOrdered(0, x)) \
- delete x; \
- else \
- static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \
- } \
- return thisGlobalStatic.pointer.load(); \
- }
-
-#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
- static TYPE *NAME() \
- { \
- static QGlobalStatic<TYPE > thisGlobalStatic \
- = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \
- if (!thisGlobalStatic.pointer.load() && !thisGlobalStatic.destroyed) { \
- TYPE *x = new TYPE ARGS; \
- if (!thisGlobalStatic.pointer.testAndSetOrdered(0, x)) \
- delete x; \
- else \
- static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \
- } \
- return thisGlobalStatic.pointer.load(); \
- }
-
-#define Q_GLOBAL_STATIC_WITH_INITIALIZER(TYPE, NAME, INITIALIZER) \
- static TYPE *NAME() \
- { \
- static QGlobalStatic<TYPE > thisGlobalStatic \
- = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \
- if (!thisGlobalStatic.pointer.load() && !thisGlobalStatic.destroyed) { \
- QScopedPointer<TYPE > x(new TYPE); \
- INITIALIZER; \
- if (thisGlobalStatic.pointer.testAndSetOrdered(0, x.data())) { \
- static QGlobalStaticDeleter<TYPE > cleanup(thisGlobalStatic); \
- x.take(); \
- } \
- } \
- return thisGlobalStatic.pointer.load(); \
- }
-
-#endif
-
Q_DECL_CONSTEXPR static inline bool qFuzzyCompare(double p1, double p2)
{
return (qAbs(p1 - p2) <= 0.000000000001 * qMin(qAbs(p1), qAbs(p2)));
@@ -1089,6 +970,9 @@ template <typename T> struct QEnableIf<true, T> { typedef T Type; };
QT_END_NAMESPACE
QT_END_HEADER
+// Q_GLOBAL_STATIC
+#include <QtCore/qglobalstatic.h>
+
// qDebug and friends
#include <QtCore/qlogging.h>
#include <QtCore/qflags.h>
diff --git a/src/corelib/global/qglobalstatic.h b/src/corelib/global/qglobalstatic.h
new file mode 100644
index 0000000000..78a4acff12
--- /dev/null
+++ b/src/corelib/global/qglobalstatic.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Intel Corporation
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qglobal.h>
+
+#ifndef QGLOBALSTATIC_H
+#define QGLOBALSTATIC_H
+
+#include <QtCore/qatomic.h>
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+/*
+ * QGlobalStatic internals:
+ *
+ * The pointer is initialized to 0.
+ * The guard is initialized to 0.
+ * The guard can assume the following values:
+ * -2: object initialized and already destroyed
+ * -1: object initialized and is still valid
+ * 0: not initialized, the value of the pointer should be null
+ * +1: initializing, must wait until a state change happens
+ * (not used in the current implementation)
+ */
+
+namespace QtGlobalStatic {
+enum GuardValues {
+ Destroyed = -2,
+ Initialized = -1,
+ Uninitialized = 0,
+ Initializing = 1
+};
+}
+
+#if defined(QT_NO_THREAD) || defined(Q_CC_GNU)
+// some compilers support thread-safe statics
+// The IA-64 C++ ABI requires this, so we know that all GCC versions since 3.4
+// support it. C++11 also requires this behavior.
+// Clang and Intel CC masquerade as GCC when compiling on Linux and Mac OS X.
+
+#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \
+ Q_DECL_HIDDEN inline Type *innerFunction() \
+ { \
+ struct HolderBase { \
+ ~HolderBase() Q_DECL_NOTHROW \
+ { guard.store(QtGlobalStatic::Destroyed); } \
+ }; \
+ static struct Holder : public HolderBase { \
+ Type value; \
+ Holder() \
+ Q_DECL_NOEXCEPT_EXPR(noexcept(Type ARGS)) \
+ : value ARGS \
+ { guard.store(QtGlobalStatic::Initialized); } \
+ } holder; \
+ return &holder.value; \
+ }
+#else
+// We don't know if this compiler supports thread-safe global statics
+// so use our own locked implementation
+
+QT_END_NAMESPACE
+#include <QtCore/qmutex.h>
+QT_BEGIN_NAMESPACE
+
+#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \
+ Q_DECL_HIDDEN inline Type *innerFunction() \
+ { \
+ static Type *d; \
+ static QBasicMutex mutex; \
+ int x = guard.loadAcquire(); \
+ if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) { \
+ QMutexLocker locker(&mutex); \
+ if (guard.load() == QtGlobalStatic::Uninitialized) { \
+ d = new Type ARGS; \
+ static struct Cleanup { \
+ ~Cleanup() { \
+ delete d; \
+ guard.store(QtGlobalStatic::Destroyed); \
+ } \
+ } cleanup; \
+ guard.store(QtGlobalStatic::Initialized); \
+ } \
+ } \
+ return d; \
+ }
+#endif
+
+// this class must be POD, unless the compiler supports thread-safe statics
+template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
+struct QGlobalStatic
+{
+ typedef T Type;
+
+ bool isDestroyed() const { return guard.load() <= QtGlobalStatic::Destroyed; }
+ bool exists() const { return guard.load() == QtGlobalStatic::Initialized; }
+ operator Type *() { if (isDestroyed()) return 0; return innerFunction(); }
+ Type *operator()() { if (isDestroyed()) return 0; return innerFunction(); }
+ Type *operator->() { return innerFunction(); }
+ Type &operator*() { return *innerFunction(); }
+};
+
+#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
+ namespace { namespace Q_QGS_ ## NAME { \
+ typedef TYPE Type; \
+ QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
+ Q_GLOBAL_STATIC_INTERNAL(ARGS) \
+ } } \
+ static QGlobalStatic<TYPE, \
+ Q_QGS_ ## NAME::innerFunction, \
+ Q_QGS_ ## NAME::guard> NAME;
+
+#define Q_GLOBAL_STATIC(TYPE, NAME) \
+ Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif // QGLOBALSTATIC_H
diff --git a/tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro b/tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro
new file mode 100644
index 0000000000..21cab8f67d
--- /dev/null
+++ b/tests/auto/corelib/global/qglobalstatic/qglobalstatic.pro
@@ -0,0 +1,12 @@
+QT += testlib core-private
+
+QT -= gui
+
+TARGET = tst_qglobalstatic
+CONFIG += console
+CONFIG -= app_bundle
+CONFIG += exceptions
+
+SOURCES += tst_qglobalstatic.cpp
+DEFINES += SRCDIR=\\\"$$PWD/\\\"
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp b/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp
new file mode 100644
index 0000000000..b9aa70fe80
--- /dev/null
+++ b/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp
@@ -0,0 +1,214 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Thiago Macieira <thiago@kde.org>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QThread>
+#include <QtTest/QtTest>
+
+class tst_QGlobalStatic : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void beforeInitialization();
+ void api();
+ void constVolatile();
+ void exception();
+ void threadStressTest();
+ void afterDestruction();
+};
+
+Q_GLOBAL_STATIC_WITH_ARGS(const int, constInt, (42))
+Q_GLOBAL_STATIC_WITH_ARGS(volatile int, volatileInt, (-47))
+
+void otherFunction()
+{
+ // never called
+ constInt();
+ volatileInt();
+}
+
+// do not initialize the following Q_GLOBAL_STATIC
+Q_GLOBAL_STATIC(int, checkedBeforeInitialization)
+void tst_QGlobalStatic::beforeInitialization()
+{
+ QVERIFY(!checkedBeforeInitialization.exists());
+ QVERIFY(!checkedBeforeInitialization.isDestroyed());
+}
+
+struct Type {
+ int i;
+};
+
+Q_GLOBAL_STATIC(Type, checkedAfterInitialization)
+void tst_QGlobalStatic::api()
+{
+ // check the API
+ QVERIFY((Type *)checkedAfterInitialization);
+ QVERIFY(checkedAfterInitialization());
+ *checkedAfterInitialization = Type();
+ *checkedAfterInitialization() = Type();
+
+ checkedAfterInitialization()->i = 47;
+ checkedAfterInitialization->i = 42;
+ QCOMPARE(checkedAfterInitialization()->i, 42);
+ checkedAfterInitialization()->i = 47;
+ QCOMPARE(checkedAfterInitialization->i, 47);
+
+ QVERIFY(checkedAfterInitialization.exists());
+ QVERIFY(!checkedAfterInitialization.isDestroyed());
+}
+
+void tst_QGlobalStatic::constVolatile()
+{
+ QCOMPARE(*constInt(), 42);
+ QCOMPARE((int)*volatileInt(), -47);
+ QCOMPARE(*constInt(), 42);
+ QCOMPARE((int)*volatileInt(), -47);
+}
+
+struct ThrowingType
+{
+ static QBasicAtomicInt constructedCount;
+ static QBasicAtomicInt destructedCount;
+ ThrowingType(QBasicAtomicInt &throwControl)
+ {
+ constructedCount.ref();
+ if (throwControl.fetchAndAddRelaxed(-1) != 0)
+ throw 0;
+ }
+ ~ThrowingType() { destructedCount.ref(); }
+};
+QBasicAtomicInt ThrowingType::constructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
+QBasicAtomicInt ThrowingType::destructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+QBasicAtomicInt exceptionControlVar = Q_BASIC_ATOMIC_INITIALIZER(1);
+Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, exceptionGS, (exceptionControlVar))
+void tst_QGlobalStatic::exception()
+{
+ if (exceptionControlVar.load() != 1)
+ QSKIP("This test cannot be run more than once");
+ ThrowingType::constructedCount.store(0);
+ ThrowingType::destructedCount.store(0);
+
+ bool exceptionCaught = false;
+ try {
+ exceptionGS();
+ } catch (int) {
+ exceptionCaught = true;
+ }
+ QCOMPARE(ThrowingType::constructedCount.load(), 1);
+ QVERIFY(exceptionCaught);
+
+ exceptionGS();
+ QCOMPARE(ThrowingType::constructedCount.load(), 2);
+}
+
+QBasicAtomicInt threadStressTestControlVar = Q_BASIC_ATOMIC_INITIALIZER(5);
+Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, threadStressTestGS, (threadStressTestControlVar))
+
+
+void tst_QGlobalStatic::threadStressTest()
+{
+ class ThreadStressTestThread: public QThread
+ {
+ public:
+ QReadWriteLock *lock;
+ void run()
+ {
+ QReadLocker l(lock);
+ //usleep(qrand() * 200 / RAND_MAX);
+ // thundering herd
+ try {
+ threadStressTestGS();
+ } catch (int) {
+ }
+ }
+ };
+
+ ThrowingType::constructedCount.store(0);
+ ThrowingType::destructedCount.store(0);
+ int expectedConstructionCount = threadStressTestControlVar.load() + 1;
+ if (expectedConstructionCount <= 0)
+ QSKIP("This test cannot be run more than once");
+
+ const int numThreads = 200;
+ ThreadStressTestThread threads[numThreads];
+ QReadWriteLock lock;
+ lock.lockForWrite();
+ for (int i = 0; i < numThreads; ++i) {
+ threads[i].lock = &lock;
+ threads[i].start();
+ }
+
+ // wait for all threads
+ // release the herd
+ lock.unlock();
+
+ for (int i = 0; i < numThreads; ++i)
+ threads[i].wait();
+
+ QCOMPARE(ThrowingType::constructedCount.loadAcquire(), expectedConstructionCount);
+ QCOMPARE(ThrowingType::destructedCount.loadAcquire(), 0);
+}
+
+Q_GLOBAL_STATIC(int, checkedAfterDestruction)
+void tst_QGlobalStatic::afterDestruction()
+{
+ // this test will not produce results now
+ // it will simply run some code on destruction (after the global statics have been deleted)
+ // if that fails, this will cause a crash
+
+ // static destruction is LIFO: so we must add our exit-time code before the
+ // global static is used for the first time
+ static struct RunAtExit {
+ ~RunAtExit() {
+ int *ptr = checkedAfterDestruction();
+ if (ptr)
+ qFatal("Global static is not null as was expected");
+ }
+ } runAtExit;
+ (void) runAtExit;
+
+ *checkedAfterDestruction = 42;
+}
+
+QTEST_APPLESS_MAIN(tst_QGlobalStatic);
+
+#include "tst_qglobalstatic.moc"
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index 581644b19f..d7add383e1 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -144,6 +144,16 @@ private slots:
void disconnectDoesNotLeakFunctor();
};
+struct QObjectCreatedOnShutdown
+{
+ QObjectCreatedOnShutdown() {}
+ ~QObjectCreatedOnShutdown()
+ {
+ QObject();
+ }
+};
+static QObjectCreatedOnShutdown s_qobjectCreatedOnShutdown;
+
class SenderObject : public QObject
{
Q_OBJECT