summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorShane Kearns <shane.kearns@accenture.com>2011-11-30 13:39:23 (GMT)
committerShane Kearns <shane.kearns@accenture.com>2011-11-30 14:47:43 (GMT)
commit67d146092a6c11f1ca3c622ae0737202ca292a58 (patch)
tree170825dfe25f77557e69b7f77ffba0a442260af4 /src/network
parent951e46bc21168cb66780f932006ec41b7dfea234 (diff)
downloadQt-67d146092a6c11f1ca3c622ae0737202ca292a58.zip
Qt-67d146092a6c11f1ca3c622ae0737202ca292a58.tar.gz
Qt-67d146092a6c11f1ca3c622ae0737202ca292a58.tar.bz2
Backport SMP safe initialisation of QNetworkConfigurationManager
The original commit in Qt5 uses new atomics API that is not in Qt4. I have replaced with equivalent calls. Also the init/cleanup functions are different, so those are renamed. Reviewed-by: mread Task-number: ou1cimx1#946677 Original commit message follows: Author: Thiago Macieira <thiago@kde.org> Date: Wed Jul 6 00:06:15 2011 +0200 Fix the QNetworkConnectionManagerPrivate initialisation code. The current code was meant to be a thread-safe initialisation that also ran a couple of extra steps. But it wasn't. While it's ok to call qAddPostRoutine(), the call to updateConfigurations() was thread-unsafe. It is possible that another thread got the pointer to the Private before updateConfigurations() finished. So instead protect the initialisation with a mutex. It's possible that the value of the pointer becomes visible to other processors before the other contained values, so use atomics here. To call qAddPostRoutine safely from the main thread, use the trick of deleteLater() (which is thread-safe) in another thread connecting to a slot.
Diffstat (limited to 'src/network')
-rw-r--r--src/network/bearer/qnetworkconfigmanager.cpp66
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.h3
2 files changed, 47 insertions, 22 deletions
diff --git a/src/network/bearer/qnetworkconfigmanager.cpp b/src/network/bearer/qnetworkconfigmanager.cpp
index 8065025..da09a9c 100644
--- a/src/network/bearer/qnetworkconfigmanager.cpp
+++ b/src/network/bearer/qnetworkconfigmanager.cpp
@@ -46,34 +46,56 @@
#include <QtCore/qstringlist.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qthread.h>
+#include <QtCore/private/qcoreapplication_p.h>
#ifndef QT_NO_BEARERMANAGEMENT
QT_BEGIN_NAMESPACE
-#define Q_GLOBAL_STATIC_QAPP_DESTRUCTION(TYPE, NAME) \
- static QGlobalStatic<TYPE > this_##NAME \
- = { Q_BASIC_ATOMIC_INITIALIZER(0), false }; \
- static void NAME##_cleanup() \
- { \
- this_##NAME.pointer->cleanup(); \
- this_##NAME.pointer = 0; \
- } \
- static TYPE *NAME() \
- { \
- if (!this_##NAME.pointer) { \
- TYPE *x = new TYPE; \
- if (!this_##NAME.pointer.testAndSetOrdered(0, x)) \
- delete x; \
- else { \
- qAddPostRoutine(NAME##_cleanup); \
- this_##NAME.pointer->initialize(); \
- } \
- } \
- return this_##NAME.pointer; \
- }
+static QBasicAtomicPointer<QNetworkConfigurationManagerPrivate> connManager_ptr;
+Q_GLOBAL_STATIC(QMutex, connManager_mutex)
+
+static void connManager_cleanup()
+{
+ // this is not atomic or thread-safe!
+ if(connManager_ptr)
+ connManager_ptr->cleanup();
+ connManager_ptr = 0;
+}
+
+void QNetworkConfigurationManagerPrivate::addPostRoutine()
+{
+ qAddPostRoutine(connManager_cleanup);
+}
-Q_GLOBAL_STATIC_QAPP_DESTRUCTION(QNetworkConfigurationManagerPrivate, connManager);
+static QNetworkConfigurationManagerPrivate *connManager()
+{
+ QNetworkConfigurationManagerPrivate *ptr = connManager_ptr.fetchAndAddAcquire(0);
+ if (!ptr) {
+ QMutexLocker locker(connManager_mutex());
+ if (!(ptr = connManager_ptr.fetchAndAddAcquire(0))) {
+ ptr = new QNetworkConfigurationManagerPrivate;
+
+ if (QCoreApplicationPrivate::mainThread() == QThread::currentThread()) {
+ // right thread or no main thread yet
+ ptr->addPostRoutine();
+ ptr->initialize();
+ } else {
+ // wrong thread, we need to make the main thread do this
+ QObject *obj = new QObject;
+ QObject::connect(obj, SIGNAL(destroyed()), ptr, SLOT(addPostRoutine()), Qt::DirectConnection);
+ ptr->initialize(); // this moves us to the right thread
+ obj->moveToThread(QCoreApplicationPrivate::mainThread());
+ obj->deleteLater();
+ }
+
+ connManager_ptr.fetchAndStoreRelease(ptr);
+ }
+ }
+ return ptr;
+}
QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
{
diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h
index 56f66b4..ded1701 100644
--- a/src/network/bearer/qnetworkconfigmanager_p.h
+++ b/src/network/bearer/qnetworkconfigmanager_p.h
@@ -94,6 +94,8 @@ public:
public Q_SLOTS:
void updateConfigurations();
+ static void addPostRoutine();
+
Q_SIGNALS:
void configurationAdded(const QNetworkConfiguration &config);
void configurationRemoved(const QNetworkConfiguration &config);
@@ -108,6 +110,7 @@ private Q_SLOTS:
void pollEngines();
+
private:
Q_INVOKABLE void startPolling();
QTimer *pollTimer;