bearer: run the bearer engines in their own worker thread

The original architecture of the QtNetwork bearer support hosted the
engines in the application's main thread, but this causes some problems.
If the QNetworkConfigurationManager is constructed in a worker thread,
then it is populated asynchronously without any notification when it is
done (the app gets incomplete or missing results)
Fixing that by restoring the earlier behaviour of using blocking queued
connections to wait for the lists to be populated caused a regression,
as some applications deadlock because the main thread is waiting on the
worker thread at this time.

By introducing a dedicated worker thread for the bearer engines,
QNetworkConfigurationManager can be safely constructed in any thread
while using blocking queued connections internally.

Task-number: QTBUG-18795
Change-Id: Iaa1706d44b02b42057c100b0b399364175af2ddb
Reviewed-by: mread
(cherry picked from commit 5f879c55e531165cc2569b03c3796d0f33d0a0b7)
Reviewed-by: Jonas Gastal <jgastal@profusion.mobi>
Reviewed-by: Murray Read <ext-murray.2.read@nokia.com>
Reviewed-by: Alex <alex.blasche@nokia.com>
This commit is contained in:
Shane Kearns 2011-06-01 16:35:43 +01:00 committed by Qt by Nokia
parent e52ef6788b
commit f6b30722fa
3 changed files with 30 additions and 18 deletions

View File

@ -60,8 +60,9 @@ Q_GLOBAL_STATIC(QMutex, connManager_mutex)
static void connManager_cleanup() static void connManager_cleanup()
{ {
// this is not atomic or thread-safe! // this is not atomic or thread-safe!
delete connManager_ptr.load(); QNetworkConfigurationManagerPrivate *cmp = connManager_ptr.fetchAndStoreAcquire(0);
connManager_ptr.store(0); if (cmp)
cmp->cleanup();
} }
void QNetworkConfigurationManagerPrivate::addPostRoutine() void QNetworkConfigurationManagerPrivate::addPostRoutine()
@ -80,12 +81,12 @@ QNetworkConfigurationManagerPrivate *qNetworkConfigurationManagerPrivate()
if (QCoreApplicationPrivate::mainThread() == QThread::currentThread()) { if (QCoreApplicationPrivate::mainThread() == QThread::currentThread()) {
// right thread or no main thread yet // right thread or no main thread yet
ptr->addPostRoutine(); ptr->addPostRoutine();
ptr->updateConfigurations(); ptr->initialize();
} else { } else {
// wrong thread, we need to make the main thread do this // wrong thread, we need to make the main thread do this
QObject *obj = new QObject; QObject *obj = new QObject;
QObject::connect(obj, SIGNAL(destroyed()), ptr, SLOT(addPostRoutine()), Qt::DirectConnection); QObject::connect(obj, SIGNAL(destroyed()), ptr, SLOT(addPostRoutine()), Qt::DirectConnection);
ptr->updateConfigurations(); // this moves us to the main thread ptr->initialize(); // this moves us to the right thread
obj->moveToThread(QCoreApplicationPrivate::mainThread()); obj->moveToThread(QCoreApplicationPrivate::mainThread());
obj->deleteLater(); obj->deleteLater();
} }

View File

@ -66,11 +66,31 @@ QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate()
qRegisterMetaType<QNetworkConfigurationPrivatePointer>("QNetworkConfigurationPrivatePointer"); qRegisterMetaType<QNetworkConfigurationPrivatePointer>("QNetworkConfigurationPrivatePointer");
} }
void QNetworkConfigurationManagerPrivate::initialize()
{
//Two stage construction, because we only want to do this heavyweight work for the winner of the Q_GLOBAL_STATIC race.
bearerThread = new QThread();
bearerThread->moveToThread(QCoreApplicationPrivate::mainThread()); // because cleanup() is called in main thread context.
moveToThread(bearerThread);
bearerThread->start();
updateConfigurations();
}
QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate()
{ {
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
qDeleteAll(sessionEngines); qDeleteAll(sessionEngines);
if (bearerThread)
bearerThread->quit();
}
void QNetworkConfigurationManagerPrivate::cleanup()
{
QThread* thread = bearerThread;
deleteLater();
if (thread->wait(5000))
delete thread;
} }
QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() const QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration() const
@ -350,13 +370,6 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations()
if (qobject_cast<QBearerEngine *>(sender())) if (qobject_cast<QBearerEngine *>(sender()))
return; return;
if (thread() != QCoreApplicationPrivate::mainThread()) {
if (thread() != QThread::currentThread())
return;
moveToThread(QCoreApplicationPrivate::mainThread());
}
updating = false; updating = false;
#ifndef QT_NO_LIBRARY #ifndef QT_NO_LIBRARY
@ -375,7 +388,7 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations()
else else
sessionEngines.append(engine); sessionEngines.append(engine);
engine->moveToThread(QCoreApplicationPrivate::mainThread()); engine->moveToThread(bearerThread);
connect(engine, SIGNAL(updateCompleted()), connect(engine, SIGNAL(updateCompleted()),
this, SLOT(updateConfigurations())); this, SLOT(updateConfigurations()));
@ -411,14 +424,9 @@ void QNetworkConfigurationManagerPrivate::updateConfigurations()
if (firstUpdate) { if (firstUpdate) {
firstUpdate = false; firstUpdate = false;
QList<QBearerEngine*> enginesToInitialize = sessionEngines; //shallow copy the list in case it is modified when we unlock mutex QList<QBearerEngine*> enginesToInitialize = sessionEngines; //shallow copy the list in case it is modified when we unlock mutex
Qt::ConnectionType connectionType;
if (QCoreApplicationPrivate::mainThread() == QThread::currentThread())
connectionType = Qt::DirectConnection;
else
connectionType = Qt::BlockingQueuedConnection;
locker.unlock(); locker.unlock();
foreach (QBearerEngine* engine, enginesToInitialize) { foreach (QBearerEngine* engine, enginesToInitialize) {
QMetaObject::invokeMethod(engine, "initialize", connectionType); QMetaObject::invokeMethod(engine, "initialize", Qt::BlockingQueuedConnection);
} }
} }
} }

View File

@ -89,6 +89,8 @@ public:
void enablePolling(); void enablePolling();
void disablePolling(); void disablePolling();
void initialize();
void cleanup();
public Q_SLOTS: public Q_SLOTS:
void updateConfigurations(); void updateConfigurations();
@ -112,6 +114,7 @@ private Q_SLOTS:
private: private:
Q_INVOKABLE void startPolling(); Q_INVOKABLE void startPolling();
QTimer *pollTimer; QTimer *pollTimer;
QThread *bearerThread;
private: private:
mutable QMutex mutex; mutable QMutex mutex;