From 847df81a5680fe4d71196d0afe5e68e41ae49700 Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Thu, 7 Apr 2011 15:10:31 +0100 Subject: Implement support for sockets started before the event loop If there is no event loop when a socket notification is enabled, then invoke the method via a queued connection so that it is run again when the event loop is started. This covers sockets created and having an asynchronous API called before calling QCoreApplication::exec(). Reviewed-by: Markus Goetz --- src/network/socket/qsymbiansocketengine.cpp | 69 +++++++++++------------------ src/network/socket/qsymbiansocketengine_p.h | 2 +- tests/auto/qtcpsocket/tst_qtcpsocket.cpp | 45 ++++++++++++++++++- 3 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/network/socket/qsymbiansocketengine.cpp b/src/network/socket/qsymbiansocketengine.cpp index 165316d..f1b2982 100644 --- a/src/network/socket/qsymbiansocketengine.cpp +++ b/src/network/socket/qsymbiansocketengine.cpp @@ -1433,6 +1433,26 @@ void QSymbianSocketEnginePrivate::setError(TInt symbianError) hasSetSocketError = true; } +void QSymbianSocketEngine::startNotifications() +{ + Q_D(QSymbianSocketEngine); +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QSymbianSocketEngine::startNotifications" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled; +#endif + if (!d->asyncSelect && (d->readNotificationsEnabled || d->writeNotificationsEnabled || d->exceptNotificationsEnabled)) { + if (d->threadData->eventDispatcher) { + d->asyncSelect = q_check_ptr(new QAsyncSelect( + static_cast (d->threadData->eventDispatcher), d->nativeSocket, + this)); + } else { + // call again when event dispatcher has been created + QMetaObject::invokeMethod(this, "startNotifications", Qt::QueuedConnection); + } + } + if (d->asyncSelect) + d->asyncSelect->IssueRequest(); +} + bool QSymbianSocketEngine::isReadNotificationEnabled() const { Q_D(const QSymbianSocketEngine); @@ -1448,13 +1468,7 @@ void QSymbianSocketEngine::setReadNotificationEnabled(bool enable) qDebug() << "QSymbianSocketEngine::setReadNotificationEnabled" << enable << "socket" << d->socketDescriptor; #endif d->readNotificationsEnabled = enable; - if (enable && d->threadData->eventDispatcher && !d->asyncSelect) - d->asyncSelect = q_check_ptr( - new QAsyncSelect(static_cast(d->threadData->eventDispatcher), - d->nativeSocket, this)); - // TODO: what do we do if event dispatcher doesn't exist yet? - if (d->asyncSelect) - d->asyncSelect->IssueRequest(); + startNotifications(); } bool QSymbianSocketEngine::isWriteNotificationEnabled() const @@ -1472,13 +1486,7 @@ void QSymbianSocketEngine::setWriteNotificationEnabled(bool enable) qDebug() << "QSymbianSocketEngine::setWriteNotificationEnabled" << enable << "socket" << d->socketDescriptor; #endif d->writeNotificationsEnabled = enable; - if (enable && d->threadData->eventDispatcher && !d->asyncSelect) - d->asyncSelect = q_check_ptr( - new QAsyncSelect(static_cast(d->threadData->eventDispatcher), - d->nativeSocket, this)); - // TODO: what do we do if event dispatcher doesn't exist yet? - if (d->asyncSelect) - d->asyncSelect->IssueRequest(); + startNotifications(); } bool QSymbianSocketEngine::isExceptionNotificationEnabled() const @@ -1489,7 +1497,6 @@ bool QSymbianSocketEngine::isExceptionNotificationEnabled() const return false; } -// FIXME do we really need this for symbian? void QSymbianSocketEngine::setExceptionNotificationEnabled(bool enable) { Q_D(QSymbianSocketEngine); @@ -1498,12 +1505,7 @@ void QSymbianSocketEngine::setExceptionNotificationEnabled(bool enable) qDebug() << "QSymbianSocketEngine::setExceptionNotificationEnabled" << enable << "socket" << d->socketDescriptor; #endif d->exceptNotificationsEnabled = enable; - if (enable && d->threadData->eventDispatcher && !d->asyncSelect) - d->asyncSelect = q_check_ptr( - new QAsyncSelect(static_cast(d->threadData->eventDispatcher), - d->nativeSocket, this)); - if (d->asyncSelect) - d->asyncSelect->IssueRequest(); + startNotifications(); } bool QSymbianSocketEngine::waitForRead(int msecs, bool *timedOut) @@ -1588,38 +1590,21 @@ qint64 QSymbianSocketEngine::bytesToWrite() const return 0; } -//TODO: is defining PostThreadChangeEvent as QEvent::User + 1 safe? -//TODO: would QMetaObject::invokeMethod(obj, "postThreadChangeSlot", Qt::QueuedConnection) be better? -//TODO: if QTBUG-16787 is implemented, use that instead bool QSymbianSocketEngine::event(QEvent* ev) { Q_D(QSymbianSocketEngine); + if (ev->type() == QEvent::ThreadChange) { #ifdef QNATIVESOCKETENGINE_DEBUG - qDebug() << "QSymbianSocketEngine::event"; -#endif - switch (ev->type()) { - case QEvent::ThreadChange: -#ifdef QNATIVESOCKETENGINE_DEBUG - qDebug() << "ThreadChange" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled; + qDebug() << "QSymbianSocketEngine::event - ThreadChange" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled; #endif if (d->asyncSelect) { delete d->asyncSelect; d->asyncSelect = 0; - QEvent *postThreadChangeEvent = new QEvent(PostThreadChangeEvent); - QCoreApplication::postEvent(this, postThreadChangeEvent); + // recreate select in new thread (because it is queued, the method is called in the new thread context) + QMetaObject::invokeMethod(this, "startNotifications", Qt::QueuedConnection); } d->selectTimer.Close(); return true; - case PostThreadChangeEvent: -#ifdef QNATIVESOCKETENGINE_DEBUG - qDebug() << "PostThreadChangeEvent" << d->readNotificationsEnabled << d->writeNotificationsEnabled << d->exceptNotificationsEnabled; -#endif - // recreate select in new thread - d->asyncSelect = q_check_ptr( - new QAsyncSelect(static_cast(d->threadData->eventDispatcher), - d->nativeSocket, this)); - d->asyncSelect->IssueRequest(); - return true; } return QAbstractSocketEngine::event(ev); } diff --git a/src/network/socket/qsymbiansocketengine_p.h b/src/network/socket/qsymbiansocketengine_p.h index bbe1269..85ab54a 100644 --- a/src/network/socket/qsymbiansocketengine_p.h +++ b/src/network/socket/qsymbiansocketengine_p.h @@ -134,7 +134,7 @@ public: bool event(QEvent* ev); - static const QEvent::Type PostThreadChangeEvent = (QEvent::Type)(QEvent::User + 1); + Q_INVOKABLE void startNotifications(); public Q_SLOTS: // TODO: Why do we do this? This is private Qt implementation stuff anyway, no need for it diff --git a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp index 6852e29..623e02b 100644 --- a/tests/auto/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/qtcpsocket/tst_qtcpsocket.cpp @@ -106,6 +106,7 @@ Q_DECLARE_METATYPE(QList) //TESTED_FILES= QT_FORWARD_DECLARE_CLASS(QTcpSocket) +QT_FORWARD_DECLARE_CLASS(SocketPair) class tst_QTcpSocket : public QObject { @@ -138,6 +139,7 @@ public slots: void init(); void cleanup(); private slots: + void socketsConstructedBeforeEventLoop(); void constructing(); void setInvalidSocketDescriptor(); void setSocketDescriptor(); @@ -221,6 +223,8 @@ protected slots: void abortiveClose_abortSlot(); void remoteCloseErrorSlot(); void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); + void earlySocketBytesSent(qint64 bytes); + void earlySocketReadyRead(); private: QByteArray expectedReplyIMAP(); @@ -243,6 +247,10 @@ private: bool gotClosedSignal; int numConnections; static int loopLevel; + + SocketPair *earlyConstructedSockets; + int earlyBytesWrittenCount; + int earlyReadyReadCount; }; enum ProxyTests { @@ -296,8 +304,16 @@ public: tst_QTcpSocket::tst_QTcpSocket() { - Q_SET_DEFAULT_IAP tmpSocket = 0; + + //This code relates to the socketsConstructedBeforeEventLoop test case + earlyConstructedSockets = new SocketPair; + QVERIFY(earlyConstructedSockets->create()); + earlyBytesWrittenCount = 0; + earlyReadyReadCount = 0; + connect(earlyConstructedSockets->endPoints[0], SIGNAL(readyRead()), this, SLOT(earlySocketReadyRead())); + connect(earlyConstructedSockets->endPoints[1], SIGNAL(bytesWritten(qint64)), this, SLOT(earlySocketBytesSent(qint64))); + earlyConstructedSockets->endPoints[1]->write("hello work"); } tst_QTcpSocket::~tst_QTcpSocket() @@ -399,6 +415,33 @@ void tst_QTcpSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthent //---------------------------------------------------------------------------------- +void tst_QTcpSocket::socketsConstructedBeforeEventLoop() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH_GLOBAL(bool, ssl); + if (setProxy || ssl) + return; + //This test checks that sockets constructed before QCoreApplication::exec() still emit signals + //see construction code in the tst_QTcpSocket constructor + enterLoop(3); + QCOMPARE(earlyBytesWrittenCount, 1); + QCOMPARE(earlyReadyReadCount, 1); + earlyConstructedSockets->endPoints[0]->close(); + earlyConstructedSockets->endPoints[1]->close(); +} + +void tst_QTcpSocket::earlySocketBytesSent(qint64 bytes) +{ + earlyBytesWrittenCount++; +} + +void tst_QTcpSocket::earlySocketReadyRead() +{ + earlyReadyReadCount++; +} + +//---------------------------------------------------------------------------------- + void tst_QTcpSocket::constructing() { QTcpSocket *socket = newSocket(); -- cgit v0.12