From e1f691d84dad17c5ee47c97c31ae743093ad8bc9 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Thu, 8 Oct 2009 09:15:20 +0200 Subject: Ensure that qmake doesn't lose the error code when processing subdirs When processing a project in a subdirs template failed for whatever reason then qmake would lose the result of that and would return an error code of 0 if the subdirs project file itself was processed fine. So now it ensures that any errors arising from processing a project referenced in a subdirs project file are not lost so that the error code returned from qmake will indicate an error actually occured. Task-number: QTBUG-4065 Reviewed-by: mariusSO Original-commit: c15b370c9db16fdbfd9e7bec89ee9bf8c1110827 --- qmake/generators/metamakefile.cpp | 17 ++++++++++++----- qmake/generators/metamakefile.h | 2 +- qmake/main.cpp | 6 +++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/qmake/generators/metamakefile.cpp b/qmake/generators/metamakefile.cpp index 5915fcf..819cdaf 100644 --- a/qmake/generators/metamakefile.cpp +++ b/qmake/generators/metamakefile.cpp @@ -291,6 +291,7 @@ SubdirsMetaMakefileGenerator::init() if(init_flag) return false; init_flag = true; + bool hasError = false; if(Option::recursive) { QString old_output_dir = Option::output_dir; @@ -336,14 +337,18 @@ SubdirsMetaMakefileGenerator::init() } qmake_setpwd(sub->input_dir); Option::output_dir = sub->output_dir; - sub_proj->read(subdir.fileName()); + bool tmpError = !sub_proj->read(subdir.fileName()); if(!sub_proj->variables()["QMAKE_FAILED_REQUIREMENTS"].isEmpty()) { fprintf(stderr, "Project file(%s) not recursed because all requirements not met:\n\t%s\n", subdir.fileName().toLatin1().constData(), sub_proj->values("QMAKE_FAILED_REQUIREMENTS").join(" ").toLatin1().constData()); delete sub; delete sub_proj; + Option::output_dir = old_output_dir; + qmake_setpwd(oldpwd); continue; + } else { + hasError |= tmpError; } sub->makefile = MetaMakefileGenerator::createMetaGenerator(sub_proj, sub_name); if(0 && sub->makefile->type() == SUBDIRSMETATYPE) { @@ -351,7 +356,7 @@ SubdirsMetaMakefileGenerator::init() } else { const QString output_name = Option::output.fileName(); Option::output.setFileName(sub->output_file); - sub->makefile->write(sub->output_dir); + hasError |= !sub->makefile->write(sub->output_dir); delete sub; qmakeClearCaches(); sub = 0; @@ -376,7 +381,7 @@ SubdirsMetaMakefileGenerator::init() self->makefile->init(); subs.append(self); - return true; + return !hasError; } bool @@ -482,7 +487,7 @@ MetaMakefileGenerator::createMakefileGenerator(QMakeProject *proj, bool noIO) } MetaMakefileGenerator * -MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op) +MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &name, bool op, bool *success) { MetaMakefileGenerator *ret = 0; if ((Option::qmake_mode == Option::QMAKE_GENERATE_MAKEFILE || @@ -492,7 +497,9 @@ MetaMakefileGenerator::createMetaGenerator(QMakeProject *proj, const QString &na } if (!ret) ret = new BuildsMetaMakefileGenerator(proj, name, op); - ret->init(); + bool res = ret->init(); + if (success) + *success = res; return ret; } diff --git a/qmake/generators/metamakefile.h b/qmake/generators/metamakefile.h index e69304a..f74f4a2 100644 --- a/qmake/generators/metamakefile.h +++ b/qmake/generators/metamakefile.h @@ -62,7 +62,7 @@ public: virtual ~MetaMakefileGenerator(); - static MetaMakefileGenerator *createMetaGenerator(QMakeProject *proj, const QString &name, bool op=true); + static MetaMakefileGenerator *createMetaGenerator(QMakeProject *proj, const QString &name, bool op=true, bool *success = 0); static MakefileGenerator *createMakefileGenerator(QMakeProject *proj, bool noIO = false); inline QMakeProject *projectFile() const { return project; } diff --git a/qmake/main.cpp b/qmake/main.cpp index 73fdda9..a0346c5 100644 --- a/qmake/main.cpp +++ b/qmake/main.cpp @@ -168,7 +168,11 @@ int runQMake(int argc, char **argv) continue; } - MetaMakefileGenerator *mkfile = MetaMakefileGenerator::createMetaGenerator(&project, QString(), false); + bool success = true; + MetaMakefileGenerator *mkfile = MetaMakefileGenerator::createMetaGenerator(&project, QString(), false, &success); + if (!success) + exit_val = 3; + if(mkfile && !mkfile->write(oldpwd)) { if(Option::qmake_mode == Option::QMAKE_GENERATE_PROJECT) fprintf(stderr, "Unable to generate project file.\n"); -- cgit v0.12 From 26713a8b3fa4b82ea18c314c01b4d7ccb54d1125 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 26 Oct 2009 11:02:52 +0100 Subject: QDom: make tests fail only on Windows Reviewed-by: Carlos Duclos --- tests/auto/qdom/tst_qdom.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/qdom/tst_qdom.cpp b/tests/auto/qdom/tst_qdom.cpp index 0d58554e..f3a7909 100644 --- a/tests/auto/qdom/tst_qdom.cpp +++ b/tests/auto/qdom/tst_qdom.cpp @@ -322,7 +322,9 @@ void tst_QDom::toString_01_data() */ void tst_QDom::toString_01() { +#ifdef Q_OS_WIN QFAIL("make test fail instead of timing out, will be fixed later (QT-2357)"); +#endif QFETCH(QString, fileName); QFile f(fileName); -- cgit v0.12 From d0b0e0ed8ac857d78e497b74bb1c3596273c53ba Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 27 Oct 2009 15:10:46 +0100 Subject: QLibrary on Windows: Do not show error boxes when library load fails When loading a library fails, the error message "The application or DLL ... is not a valid Windows image. Please check this against your installation diskette." is shown, which is not very helpful. Task-number: QT-2357 Reviewed-by: Marius Storm-Olsen --- src/corelib/plugin/qlibrary.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 2b463a1..6496876 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -659,7 +659,10 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) #endif if (!pHnd) { #ifdef Q_OS_WIN + //avoid 'Bad Image' message box + UINT oldmode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); hTempModule = ::LoadLibraryEx((wchar_t*)QDir::toNativeSeparators(fileName).utf16(), 0, DONT_RESOLVE_DLL_REFERENCES); + SetErrorMode(oldmode); #else # if defined(Q_OS_SYMBIAN) //Guard against accidentally trying to load non-plugin libraries by making sure the stub exists -- cgit v0.12 From 2d0b29c312ddf422595ce9debb3678bb5c4d51b6 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 27 Oct 2009 16:04:29 +0100 Subject: QAbstractSocket: wait with closing until all bytes have been written only disconnect from host when all bytes have been written; i.e. not only check whether the write buffer is empty, but also check whether the socket engine has still bytes to write. This is necessary for HTTP and SOCKS5 socket engine, because they both contain an inner TCP socket which also does buffering. For the native socket engine, there is no difference with this patch. Reviewed-by: Markus Goetz Reviewed-by: Thiago Macieira --- src/network/socket/qabstractsocket.cpp | 22 +++++++++++++++------- src/network/socket/qabstractsocketengine_p.h | 2 ++ src/network/socket/qhttpsocketengine.cpp | 10 ++++++++++ src/network/socket/qhttpsocketengine_p.h | 2 ++ src/network/socket/qnativesocketengine.cpp | 6 ++++++ src/network/socket/qnativesocketengine_p.h | 2 ++ src/network/socket/qsocks5socketengine.cpp | 14 ++++++++++++++ src/network/socket/qsocks5socketengine_p.h | 2 ++ src/network/ssl/qsslsocket.cpp | 2 ++ tests/auto/qsslsocket/tst_qsslsocket.cpp | 4 +--- 10 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 9fb0b47..955fee0 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -669,11 +669,11 @@ bool QAbstractSocketPrivate::canWriteNotification() if (socketEngine) { #if defined (Q_OS_WIN) - if (!writeBuffer.isEmpty()) - socketEngine->setWriteNotificationEnabled(true); + if (!writeBuffer.isEmpty()) + socketEngine->setWriteNotificationEnabled(true); #else - if (writeBuffer.isEmpty()) - socketEngine->setWriteNotificationEnabled(false); + if (writeBuffer.isEmpty() && socketEngine->bytesToWrite() == 0) + socketEngine->setWriteNotificationEnabled(false); #endif } @@ -710,11 +710,17 @@ void QAbstractSocketPrivate::connectionNotification() bool QAbstractSocketPrivate::flush() { Q_Q(QAbstractSocket); - if (!socketEngine || !socketEngine->isValid() || writeBuffer.isEmpty()) { + if (!socketEngine || !socketEngine->isValid() || (writeBuffer.isEmpty() + && socketEngine->bytesToWrite() == 0)) { #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::flush() nothing to do: valid ? %s, writeBuffer.isEmpty() ? %s", socketEngine->isValid() ? "yes" : "no", writeBuffer.isEmpty() ? "yes" : "no"); #endif + + // this covers the case when the buffer was empty, but we had to wait for the socket engine to finish + if (state == QAbstractSocket::ClosingState) + q->disconnectFromHost(); + return false; } @@ -751,7 +757,8 @@ bool QAbstractSocketPrivate::flush() } } - if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled()) + if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled() + && !socketEngine->bytesToWrite()) socketEngine->setWriteNotificationEnabled(false); if (state == QAbstractSocket::ClosingState) q->disconnectFromHost(); @@ -2347,7 +2354,8 @@ void QAbstractSocket::disconnectFromHostImplementation() } // Wait for pending data to be written. - if (d->socketEngine && d->socketEngine->isValid() && d->writeBuffer.size() > 0) { + if (d->socketEngine && d->socketEngine->isValid() && (d->writeBuffer.size() > 0 + || d->socketEngine->bytesToWrite() > 0)) { d->socketEngine->setWriteNotificationEnabled(true); #if defined(QABSTRACTSOCKET_DEBUG) diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index c639092..14b3c81 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -126,6 +126,8 @@ public: virtual qint64 pendingDatagramSize() const = 0; #endif + virtual qint64 bytesToWrite() const = 0; + virtual int option(SocketOption option) const = 0; virtual bool setOption(SocketOption option, int value) = 0; diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index fb61dbf..5c28318 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -276,6 +276,16 @@ qint64 QHttpSocketEngine::pendingDatagramSize() const } #endif // QT_NO_UDPSOCKET +qint64 QHttpSocketEngine::bytesToWrite() const +{ + Q_D(const QHttpSocketEngine); + if (d->socket) { + return d->socket->bytesToWrite(); + } else { + return 0; + } +} + int QHttpSocketEngine::option(SocketOption option) const { Q_D(const QHttpSocketEngine); diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index a423116..76430db 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -110,6 +110,8 @@ public: qint64 pendingDatagramSize() const; #endif // QT_NO_UDPSOCKET + qint64 bytesToWrite() const; + int option(SocketOption option) const; bool setOption(SocketOption option, int value); diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index e7f8401..a150b26 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -754,6 +754,12 @@ qint64 QNativeSocketEngine::write(const char *data, qint64 size) return d->nativeWrite(data, size); } + +qint64 QNativeSocketEngine::bytesToWrite() const +{ + return 0; +} + /*! Reads up to \a maxSize bytes into \a data from the socket. Returns the number of bytes read, or -1 if an error occurred. diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 1f6a243..a03d8f1 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -135,6 +135,8 @@ public: bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; + qint64 bytesToWrite() const; + qint64 receiveBufferSize() const; void setReceiveBufferSize(qint64 bufferSize); diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 30074cf..bd60ad1 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1235,6 +1235,9 @@ void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketEr if (!readNotificationPending) connectData->readBuffer.clear(); emitReadNotification(); + data->controlSocket->close(); + // cause a disconnect in the outer socket + emitWriteNotification(); } else if (socks5State == Uninitialized || socks5State == AuthenticationMethodsSent || socks5State == Authenticating @@ -1245,6 +1248,7 @@ void QSocks5SocketEnginePrivate::_q_controlSocketError(QAbstractSocket::SocketEr } else { q_func()->setError(data->controlSocket->error(), data->controlSocket->errorString()); emitReadNotification(); + emitWriteNotification(); } } @@ -1623,6 +1627,16 @@ qint64 QSocks5SocketEngine::pendingDatagramSize() const } #endif // QT_NO_UDPSOCKET +qint64 QSocks5SocketEngine::bytesToWrite() const +{ + Q_D(const QSocks5SocketEngine); + if (d->data && d->data->controlSocket) { + return d->data->controlSocket->bytesToWrite(); + } else { + return 0; + } +} + int QSocks5SocketEngine::option(SocketOption option) const { Q_D(const QSocks5SocketEngine); diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index 7cb0920..2402517 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -100,6 +100,8 @@ public: qint64 pendingDatagramSize() const; #endif // QT_NO_UDPSOCKET + qint64 bytesToWrite() const; + int option(SocketOption option) const; bool setOption(SocketOption option, int value); diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 2c88130..608d772 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -707,6 +707,8 @@ void QSslSocket::close() qDebug() << "QSslSocket::close()"; #endif Q_D(QSslSocket); + if (d->plainSocket) + d->plainSocket->close(); QTcpSocket::close(); // must be cleared, reading/writing not possible on closed socket: diff --git a/tests/auto/qsslsocket/tst_qsslsocket.cpp b/tests/auto/qsslsocket/tst_qsslsocket.cpp index 2bd1684..db46b66 100644 --- a/tests/auto/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/qsslsocket/tst_qsslsocket.cpp @@ -1755,9 +1755,7 @@ void tst_QSslSocket::readFromClosedSocket() socket->close(); QVERIFY(!socket->bytesAvailable()); QVERIFY(!socket->bytesToWrite()); - socket->waitForDisconnected(); - QVERIFY(!socket->bytesAvailable()); - QVERIFY(!socket->bytesToWrite()); + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); } void tst_QSslSocket::writeBigChunk() -- cgit v0.12 From 2c672ea518265496a0fc7c5de63ca7dda880dd85 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 27 Oct 2009 16:06:36 +0100 Subject: QAbstractSocket: insert timer to prevent timeout This fixes a timeout that occurred on Mac with the gui event dispatcher: we were waiting for a write notification, but timed out when we were in closing state and still waiting for the socket engine to complete writing. Now we close the socket anyway after 2 seconds. Reviewed-by: Thiago Macieira --- src/network/socket/qabstractsocket.cpp | 29 +++++++++++++++++++++++++++-- src/network/socket/qabstractsocket.h | 1 + src/network/socket/qabstractsocket_p.h | 2 ++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 955fee0..89a6e91 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -462,6 +462,7 @@ QAbstractSocketPrivate::QAbstractSocketPrivate() isBuffered(false), blockingTimeout(30000), connectTimer(0), + disconnectTimer(0), connectTimeElapsed(0), hostLookupId(-1), socketType(QAbstractSocket::UnknownSocketType), @@ -497,9 +498,10 @@ void QAbstractSocketPrivate::resetSocketLayer() socketEngine = 0; cachedSocketDescriptor = -1; } - if (connectTimer) { + if (connectTimer) connectTimer->stop(); - } + if (disconnectTimer) + disconnectTimer->stop(); } /*! \internal @@ -1094,6 +1096,15 @@ void QAbstractSocketPrivate::_q_abortConnectionAttempt() } } +void QAbstractSocketPrivate::_q_forceDisconnect() +{ + Q_Q(QAbstractSocket); + if (socketEngine && socketEngine->isValid() && state == QAbstractSocket::ClosingState) { + socketEngine->close(); + q->disconnectFromHost(); + } +} + /*! \internal Reads data from the socket layer into the read buffer. Returns @@ -2356,6 +2367,20 @@ void QAbstractSocket::disconnectFromHostImplementation() // Wait for pending data to be written. if (d->socketEngine && d->socketEngine->isValid() && (d->writeBuffer.size() > 0 || d->socketEngine->bytesToWrite() > 0)) { + // hack: when we are waiting for the socket engine to write bytes (only + // possible when using Socks5 or HTTP socket engine), then close + // anyway after 2 seconds. This is to prevent a timeout on Mac, where we + // sometimes just did not get the write notifier from the underlying + // CFSocket and no progress was made. + if (d->writeBuffer.size() == 0 && d->socketEngine->bytesToWrite() > 0) { + if (!d->disconnectTimer) { + d->disconnectTimer = new QTimer(this); + connect(d->disconnectTimer, SIGNAL(timeout()), this, + SLOT(_q_forceDisconnect()), Qt::DirectConnection); + } + if (!d->disconnectTimer->isActive()) + d->disconnectTimer->start(2000); + } d->socketEngine->setWriteNotificationEnabled(true); #if defined(QABSTRACTSOCKET_DEBUG) diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 5d94a01..5cfae17 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -216,6 +216,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_startConnecting(const QHostInfo &)) Q_PRIVATE_SLOT(d_func(), void _q_abortConnectionAttempt()) Q_PRIVATE_SLOT(d_func(), void _q_testConnection()) + Q_PRIVATE_SLOT(d_func(), void _q_forceDisconnect()) #ifdef QT3_SUPPORT public: diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h index 8ccddd3..acf82bf 100644 --- a/src/network/socket/qabstractsocket_p.h +++ b/src/network/socket/qabstractsocket_p.h @@ -93,6 +93,7 @@ public: void _q_startConnecting(const QHostInfo &hostInfo); void _q_testConnection(); void _q_abortConnectionAttempt(); + void _q_forceDisconnect(); bool readSocketNotifierCalled; bool readSocketNotifierState; @@ -148,6 +149,7 @@ public: int blockingTimeout; QTimer *connectTimer; + QTimer *disconnectTimer; int connectTimeElapsed; int hostLookupId; -- cgit v0.12 From ad342b62f1b4c6b9ca0997ddb937f3871f6d875b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 27 Oct 2009 18:19:53 +0100 Subject: Make QProcess report errors from a failed subprocess start. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: João Abecasis --- src/corelib/io/qprocess_unix.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 99296c7..f040d16 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -108,6 +108,10 @@ QT_END_NAMESPACE QT_BEGIN_NAMESPACE +// POSIX requires PIPE_BUF to be 512 or larger +// so we will use 512 +static const int errorBufferMax = 512; + #ifdef Q_OS_INTEGRITY static inline char *strdup(const char *data) { @@ -752,18 +756,19 @@ void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv } // notify failure + QString error = qt_error_string(errno); #if defined (QPROCESS_DEBUG) - fprintf(stderr, "QProcessPrivate::execChild() failed, notifying parent process\n"); + fprintf(stderr, "QProcessPrivate::execChild() failed (%s), notifying parent process\n", qPrintable(error)); #endif - qt_safe_write(childStartedPipe[1], "", 1); + qt_safe_write(childStartedPipe[1], error.data(), error.length() * sizeof(QChar)); qt_safe_close(childStartedPipe[1]); childStartedPipe[1] = -1; } bool QProcessPrivate::processStarted() { - char c; - int i = qt_safe_read(childStartedPipe[0], &c, 1); + ushort buf[errorBufferMax]; + int i = qt_safe_read(childStartedPipe[0], &buf, sizeof buf); if (startupSocketNotifier) { startupSocketNotifier->setEnabled(false); startupSocketNotifier->deleteLater(); @@ -775,6 +780,11 @@ bool QProcessPrivate::processStarted() #if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::processStarted() == %s", i <= 0 ? "true" : "false"); #endif + + // did we read an error message? + if (i > 0) + q_func()->setErrorString(QString::fromUtf16(buf, i / sizeof(QChar))); + return i <= 0; } -- cgit v0.12 From b3435aa7ae1eb4a0766b82560980e49039aea1d8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 27 Oct 2009 17:54:03 +0100 Subject: Autotest: Add some debugging info as to why the subprocess fails to start --- tests/auto/qudpsocket/tst_qudpsocket.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index 7ea2163..9418be0 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -719,6 +719,8 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() QProcess serverProcess; serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(serverProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + serverProcess.errorString())); // Wait until the server has started and reports success. while (!serverProcess.canReadLine()) @@ -732,6 +734,9 @@ void tst_QUdpSocket::outOfProcessConnectedClientServerTest() clientProcess.start(QString::fromLatin1("clientserver/clientserver connectedclient %1 %2") .arg(QLatin1String("127.0.0.1")).arg(serverPort), QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(clientProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + clientProcess.errorString())); + // Wait until the server has started and reports success. while (!clientProcess.canReadLine()) QVERIFY(clientProcess.waitForReadyRead(3000)); @@ -779,6 +784,8 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() QProcess serverProcess; serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"), QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(serverProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + serverProcess.errorString())); // Wait until the server has started and reports success. while (!serverProcess.canReadLine()) @@ -792,6 +799,9 @@ void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest() clientProcess.start(QString::fromLatin1("clientserver/clientserver unconnectedclient %1 %2") .arg(QLatin1String("127.0.0.1")).arg(serverPort), QIODevice::ReadWrite | QIODevice::Text); + QVERIFY2(clientProcess.waitForStarted(3000), + qPrintable("Failed to start subprocess: " + clientProcess.errorString())); + // Wait until the server has started and reports success. while (!clientProcess.canReadLine()) QVERIFY(clientProcess.waitForReadyRead(3000)); -- cgit v0.12 From a8b5aefc48d619c1fc0ff1e97c8e3a42baccb7c0 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 27 Oct 2009 19:07:03 +0100 Subject: QDom autotests: test is not failing anymore removing the temporary QFAIL Reviewed-by: Carlos Duclos --- tests/auto/qdom/tst_qdom.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/auto/qdom/tst_qdom.cpp b/tests/auto/qdom/tst_qdom.cpp index f3a7909..6637202 100644 --- a/tests/auto/qdom/tst_qdom.cpp +++ b/tests/auto/qdom/tst_qdom.cpp @@ -322,9 +322,6 @@ void tst_QDom::toString_01_data() */ void tst_QDom::toString_01() { -#ifdef Q_OS_WIN - QFAIL("make test fail instead of timing out, will be fixed later (QT-2357)"); -#endif QFETCH(QString, fileName); QFile f(fileName); -- cgit v0.12 From 5c7345809d7f620981f92cc2e93beb14b10504a9 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 10:54:09 +0200 Subject: Add the ability for the match-rule builder to add argument matching. I'll use this feature to match the NameOwnerChanged signal from the bus. --- src/dbus/qdbusconnection.cpp | 4 ++-- src/dbus/qdbusconnection_p.h | 1 + src/dbus/qdbusintegrator.cpp | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index bb0d06f..bead369 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -609,7 +609,7 @@ bool QDBusConnection::connect(const QString &service, const QString &path, const QString owner = d->getNameOwner(service); // we don't care if the owner is empty hook.signature = signature; // it might get started later - if (!d->prepareHook(hook, key, service, owner, path, interface, name, receiver, slot, 0, false)) + if (!d->prepareHook(hook, key, service, owner, path, interface, name, QStringList(), receiver, slot, 0, false)) return false; // don't connect // avoid duplicating: @@ -663,7 +663,7 @@ bool QDBusConnection::disconnect(const QString &service, const QString &path, co QString owner = d->getNameOwner(service); // we don't care of owner is empty hook.signature = signature; - if (!d->prepareHook(hook, key, service, owner, path, interface, name, receiver, slot, 0, false)) + if (!d->prepareHook(hook, key, service, owner, path, interface, name, QStringList(), receiver, slot, 0, false)) return false; // don't disconnect // avoid duplicating: diff --git a/src/dbus/qdbusconnection_p.h b/src/dbus/qdbusconnection_p.h index ab96457..df51c27 100644 --- a/src/dbus/qdbusconnection_p.h +++ b/src/dbus/qdbusconnection_p.h @@ -278,6 +278,7 @@ public: static bool prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, const QString &service, const QString &owner, const QString &path, const QString &interface, const QString &name, + const QStringList &argMatch, QObject *receiver, const char *signal, int minMIdx, bool buildSignature); static DBusHandlerResult messageFilter(DBusConnection *, DBusMessage *, void *); diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index fb2dd77..9e41708 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -392,7 +392,7 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, v static QByteArray buildMatchRule(const QString &service, const QString & /*owner*/, const QString &objectPath, const QString &interface, - const QString &member, const QString & /*signature*/) + const QString &member, const QStringList &argMatch, const QString & /*signature*/) { QString result = QLatin1String("type='signal',"); QString keyValue = QLatin1String("%1='%2',"); @@ -406,6 +406,14 @@ static QByteArray buildMatchRule(const QString &service, const QString & /*owner if (!member.isEmpty()) result += keyValue.arg(QLatin1String("member"), member); + // add the argument string-matching now + if (!argMatch.isEmpty()) { + keyValue = QLatin1String("arg%1='%2',"); + for (int i = 0; i < argMatch.count(); ++i) + if (!argMatch.at(i).isNull()) + result += keyValue.arg(i).arg(argMatch.at(i)); + } + result.chop(1); // remove ending comma return result.toLatin1(); } @@ -1195,6 +1203,7 @@ int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedN bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, const QString &service, const QString &owner, const QString &path, const QString &interface, const QString &name, + const QStringList &argMatch, QObject *receiver, const char *signal, int minMIdx, bool buildSignature) { @@ -1235,7 +1244,7 @@ bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hoo hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) ); } - hook.matchRule = buildMatchRule(service, owner, path, interface, mname, hook.signature); + hook.matchRule = buildMatchRule(service, owner, path, interface, mname, argMatch, hook.signature); return true; // connect to this signal } @@ -2027,7 +2036,7 @@ void QDBusConnectionPrivate::connectRelay(const QString &service, const QString SignalHook hook; QString key; - if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal, + if (!prepareHook(hook, key, service, owner, path, interface, QString(), QStringList(), receiver, signal, QDBusAbstractInterface::staticMetaObject.methodCount(), true)) return; // don't connect @@ -2059,7 +2068,7 @@ void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QStri SignalHook hook; QString key; - if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal, + if (!prepareHook(hook, key, service, owner, path, interface, QString(), QStringList(), receiver, signal, QDBusAbstractInterface::staticMetaObject.methodCount(), true)) return; // don't connect -- cgit v0.12 From 735525dc51952c90846c8129b755422b288b204b Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 11:13:10 +0200 Subject: Add new public API to QDBusConnection for connecting with string matching. The bus allows us to match string arguments when receiving messages. This is very useful for the NameOwnerChanged signal, whose first argument is usually what we're interested in. By using these new functions, you can restrict receiving of signals to those that you truly want, instead of receiving NameOwnerChanged for all services registered/unregistered on the bus. --- src/dbus/qdbusconnection.cpp | 88 ++++++++++++++++++++++++++++++++++---------- src/dbus/qdbusconnection.h | 12 ++++-- 2 files changed, 78 insertions(+), 22 deletions(-) diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index bead369..3aaba68 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -557,42 +557,61 @@ QDBusPendingCall QDBusConnection::asyncCall(const QDBusMessage &message, int tim bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, const QString &name, QObject *receiver, const char *slot) { - return connect(service, path, interface, name, QString(), receiver, slot); + return connect(service, path, interface, name, QStringList(), QString(), receiver, slot); } /*! - Disconnects the signal specified by the \a service, \a path, \a interface and \a name parameters from - the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty, - denoting a disconnection from all signals of the (\a interface, \a name) pair, from all remote - applications. + \overload - Returns true if the disconnection was successful. + Connects the signal to the slot \a slot in object \a + receiver. Unlike the previous connect() overload, this function + allows one to specify the parameter signature to be connected + using the \a signature variable. The function will then verify + that this signature can be delivered to the slot specified by \a + slot and return false otherwise. + + Returns true if the connection was successful. + + \note This function verifies that the signal signature matches the + slot's parameters, but it does not verify that the actual + signal exists with the given signature in the remote + service. */ -bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString &interface, - const QString &name, QObject *receiver, const char *slot) +bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, + const QString &name, const QString &signature, + QObject *receiver, const char *slot) { - return disconnect(service, path, interface, name, QString(), receiver, slot); + return connect(service, path, interface, name, QStringList(), signature, receiver, slot); } /*! \overload + \since 4.6 Connects the signal to the slot \a slot in object \a - receiver. Unlike the other connect() overload, this function + receiver. Unlike the previous connect() overload, this function allows one to specify the parameter signature to be connected using the \a signature variable. The function will then verify that this signature can be delivered to the slot specified by \a slot and return false otherwise. + The \a argumentMatch parameter lists the string parameters to be matched, + in sequential order. Note that, to match an empty string, you need to + pass a QString that is empty but not null (i.e., QString("")). A null + QString skips matching at that position. + + Returns true if the connection was successful. + \note This function verifies that the signal signature matches the slot's parameters, but it does not verify that the actual signal exists with the given signature in the remote service. */ bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, - const QString &name, const QString &signature, + const QString &name, const QStringList &argumentMatch, const QString &signature, QObject *receiver, const char *slot) { + if (!receiver || !slot || !d || !d->connection) return false; if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) @@ -609,7 +628,7 @@ bool QDBusConnection::connect(const QString &service, const QString &path, const QString owner = d->getNameOwner(service); // we don't care if the owner is empty hook.signature = signature; // it might get started later - if (!d->prepareHook(hook, key, service, owner, path, interface, name, QStringList(), receiver, slot, 0, false)) + if (!d->prepareHook(hook, key, service, owner, path, interface, name, argumentMatch, receiver, slot, 0, false)) return false; // don't connect // avoid duplicating: @@ -634,19 +653,50 @@ bool QDBusConnection::connect(const QString &service, const QString &path, const } /*! + Disconnects the signal specified by the \a service, \a path, \a interface + and \a name parameters from the slot \a slot in object \a receiver. The + arguments must be the same as passed to the connect() function. + + Returns true if the disconnection was successful. +*/ +bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString &interface, + const QString &name, QObject *receiver, const char *slot) +{ + return disconnect(service, path, interface, name, QStringList(), QString(), receiver, slot); +} + +/*! \overload - Disconnects the signal from the slot \a slot in object \a - receiver. Unlike the other disconnect() overload, this function - allows one to specify the parameter signature to be disconnected - using the \a signature variable. The function will then verify - that this signature is connected to the slot specified by \a slot - and return false otherwise. + Disconnects the signal specified by the \a service, \a path, \a + interface, \a name, and \a signature parameters from the slot \a slot in + object \a receiver. The arguments must be the same as passed to the + connect() function. + + Returns true if the disconnection was successful. */ bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface, const QString &name, const QString &signature, QObject *receiver, const char *slot) { + return disconnect(service, path, interface, name, QStringList(), signature, receiver, slot); +} + +/*! + \overload + \since 4.6 + + Disconnects the signal specified by the \a service, \a path, \a + interface, \a name, \a argumentMatch, and \a signature parameters from + the slot \a slot in object \a receiver. The arguments must be the same as + passed to the connect() function. + + Returns true if the disconnection was successful. +*/ +bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface, + const QString &name, const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot) +{ if (!receiver || !slot || !d || !d->connection) return false; if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) @@ -663,7 +713,7 @@ bool QDBusConnection::disconnect(const QString &service, const QString &path, co QString owner = d->getNameOwner(service); // we don't care of owner is empty hook.signature = signature; - if (!d->prepareHook(hook, key, service, owner, path, interface, name, QStringList(), receiver, slot, 0, false)) + if (!d->prepareHook(hook, key, service, owner, path, interface, name, argumentMatch, receiver, slot, 0, false)) return false; // don't disconnect // avoid duplicating: diff --git a/src/dbus/qdbusconnection.h b/src/dbus/qdbusconnection.h index 85fc7c2..82ae726 100644 --- a/src/dbus/qdbusconnection.h +++ b/src/dbus/qdbusconnection.h @@ -132,15 +132,21 @@ public: bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot); - bool disconnect(const QString &service, const QString &path, const QString &interface, - const QString &name, QObject *receiver, const char *slot); - bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, const QString& signature, QObject *receiver, const char *slot); + bool connect(const QString &service, const QString &path, const QString &interface, + const QString &name, const QStringList &argumentMatch, const QString& signature, + QObject *receiver, const char *slot); + + bool disconnect(const QString &service, const QString &path, const QString &interface, + const QString &name, QObject *receiver, const char *slot); bool disconnect(const QString &service, const QString &path, const QString &interface, const QString &name, const QString& signature, QObject *receiver, const char *slot); + bool disconnect(const QString &service, const QString &path, const QString &interface, + const QString &name, const QStringList &argumentMatch, const QString& signature, + QObject *receiver, const char *slot); bool registerObject(const QString &path, QObject *object, RegisterOptions options = ExportAdaptors); -- cgit v0.12 From c180071b66d4e5c22248c488df57c6acd6aa36ed Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 19:23:51 +0200 Subject: Autotest: fix improper use of the serviceOwnerChanged signal This test was doubly wrong: it first registered a service name, then it connected to signal to watch it. You can't receive a signal if you connect to it after it's emitted... Second, it waited for any serviceOwnerChanged() signal to exit the event loop, not necessarily the one we wanted to receive. This used to work because we'd always connect to the D-Bus signal, but now we don't anymore. --- tests/auto/qdbusinterface/tst_qdbusinterface.cpp | 34 ++++++++---------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/tests/auto/qdbusinterface/tst_qdbusinterface.cpp b/tests/auto/qdbusinterface/tst_qdbusinterface.cpp index e31a3a0..62d6342 100644 --- a/tests/auto/qdbusinterface/tst_qdbusinterface.cpp +++ b/tests/auto/qdbusinterface/tst_qdbusinterface.cpp @@ -171,6 +171,13 @@ class tst_QDBusInterface: public QObject { Q_OBJECT MyObject obj; +public slots: + void testServiceOwnerChanged(const QString &service) + { + if (service == "com.example.Test") + QTestEventLoop::instance().exitLoop(); + } + private slots: void initTestCase(); @@ -235,32 +242,13 @@ void tst_QDBusInterface::invalidAfterServiceOwnerChanged() QDBusInterface invalidInterface("com.example.Test", "/"); QVERIFY(!invalidInterface.isValid()); + QTestEventLoop::instance().connect(connIface, SIGNAL(serviceOwnerChanged(QString, QString, QString)), + SLOT(exitLoop())); QVERIFY(connIface->registerService("com.example.Test") == QDBusConnectionInterface::ServiceRegistered); - QSignalSpy serviceOwnerChangedSpy(connIface, SIGNAL(serviceOwnerChanged(QString, QString, QString))); - - QEventLoop loop; - QObject::connect(connIface, SIGNAL(serviceOwnerChanged(QString, QString, QString)), - &loop, SLOT(quit())); - loop.exec(); - - // at least once, but other services might have changed while running the test, too. - QVERIFY(serviceOwnerChangedSpy.count() >= 1); - bool foundOurService = false; - for (int i = 0; i < serviceOwnerChangedSpy.count(); ++i) { - QList args = serviceOwnerChangedSpy.at(i); - QString name = args[0].toString(); - QString oldOwner = args[1].toString(); - QString newOwner = args[2].toString(); - if (name == QLatin1String("com.example.Test")) { - if (newOwner == conn.baseService()) { - foundOurService = true; - break; - } - } - } - QVERIFY(foundOurService); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); QVERIFY(!invalidInterface.isValid()); } -- cgit v0.12 From feca69fb15a4176689e4f58252361750f3444275 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 19:25:56 +0200 Subject: Autotest: add a test that tries to follow a service changing owners. I'm not sure if this used to work before... --- .../tst_qdbusabstractinterface.cpp | 56 ++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp b/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp index baf769f..d84350b 100644 --- a/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp +++ b/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp @@ -103,6 +103,8 @@ private slots: void getComplexSignal_data(); void getComplexSignal(); + void followSignal(); + void createErrors_data(); void createErrors(); @@ -432,6 +434,60 @@ void tst_QDBusAbstractInterface::getComplexSignal() QCOMPARE(s[0][0].value(), expectedValue); } +void tst_QDBusAbstractInterface::followSignal() +{ + const QString serviceToFollow = "com.trolltech.tst_qdbusabstractinterface.FollowMe"; + Pinger p = getPinger(serviceToFollow); + QVERIFY2(p, "Not connected to D-Bus"); + + QDBusConnection con = p->connection(); + QVERIFY(!con.interface()->isServiceRegistered(serviceToFollow)); + Pinger control = getPinger(""); + + // we need to connect the signal somewhere in order for D-Bus to enable the rules + QTestEventLoop::instance().connect(p.data(), SIGNAL(voidSignal()), SLOT(exitLoop())); + QTestEventLoop::instance().connect(control.data(), SIGNAL(voidSignal()), SLOT(exitLoop())); + QSignalSpy s(p.data(), SIGNAL(voidSignal())); + + emit targetObj.voidSignal(); + QTestEventLoop::instance().enterLoop(200); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // signal must not have been received because the service isn't registered + QVERIFY(s.isEmpty()); + + // now register the service + QDBusReply r = + con.interface()->registerService(serviceToFollow, QDBusConnectionInterface::DontQueueService, + QDBusConnectionInterface::DontAllowReplacement); + QVERIFY(r.isValid() && r.value() == QDBusConnectionInterface::ServiceRegistered); + QVERIFY(con.interface()->isServiceRegistered(serviceToFollow)); + + // emit the signal again: + emit targetObj.voidSignal(); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // now the signal must have been received: + QVERIFY(s.size() == 1); + QVERIFY(s.at(0).size() == 0); + s.clear(); + + // disconnect the signal + disconnect(p.data(), SIGNAL(voidSignal()), &QTestEventLoop::instance(), 0); + + // emit the signal again: + emit targetObj.voidSignal(); + QTestEventLoop::instance().enterLoop(2); + QVERIFY(!QTestEventLoop::instance().timeout()); + + // and now it mustn't have been received + QVERIFY(s.isEmpty()); + + // cleanup: + con.interface()->unregisterService(serviceToFollow); +} + void tst_QDBusAbstractInterface::createErrors_data() { QTest::addColumn("service"); -- cgit v0.12 From 95d68417bfced3458e72ab3fdbc8f97ab15dd1db Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 12:02:40 +0200 Subject: Use the new argument-based rule-matching in QDBusAbstractInterface. This allows us to listen only to the activations we're really interested in. --- src/dbus/qdbusabstractinterface.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp index 61a9d95..bea365a 100644 --- a/src/dbus/qdbusabstractinterface.cpp +++ b/src/dbus/qdbusabstractinterface.cpp @@ -279,9 +279,17 @@ QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate &d, : QDBusAbstractInterfaceBase(d, parent) { // keep track of the service owner - if (!d_func()->currentOwner.isEmpty()) - QObject::connect(d_func()->connectionPrivate(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), - this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); + if (d.isValid && + d.connection.isConnected() + && !d.service.isEmpty() + && !d.service.startsWith(QLatin1Char(':'))) + d_func()->connection.connect(QLatin1String(DBUS_SERVICE_DBUS), // service + QString(), // path + QLatin1String(DBUS_INTERFACE_DBUS), // interface + QLatin1String("NameOwnerChanged"), + QStringList() << d.service, + QString(), // signature + this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); } /*! @@ -296,9 +304,17 @@ QDBusAbstractInterface::QDBusAbstractInterface(const QString &service, const QSt con, false), parent) { // keep track of the service owner - if (d_func()->connection.isConnected()) - QObject::connect(d_func()->connectionPrivate(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), - this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); + if (d_func()->isValid && + d_func()->connection.isConnected() + && !service.isEmpty() + && !service.startsWith(QLatin1Char(':'))) + d_func()->connection.connect(QLatin1String(DBUS_SERVICE_DBUS), // service + QString(), // path + QLatin1String(DBUS_INTERFACE_DBUS), // interface + QLatin1String("NameOwnerChanged"), + QStringList() << service, + QString(), //signature + this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); } /*! -- cgit v0.12 From 26c5680cf5adbf0c87357d84f2e2a6256724d4b7 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 19:28:35 +0200 Subject: Fix the use of the owner when connecting the service "watcher". Use null services to indicate we're not interested in the owner, but empty-but-not-null to indicate we don't know what the owner is. Since empty service names are not valid, this will mean that this rule won't match. --- src/dbus/qdbusabstractinterface.cpp | 11 +++++++++-- src/dbus/qdbusintegrator.cpp | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp index bea365a..994da10 100644 --- a/src/dbus/qdbusabstractinterface.cpp +++ b/src/dbus/qdbusabstractinterface.cpp @@ -560,9 +560,16 @@ void QDBusAbstractInterface::connectNotify(const char *signal) return; QDBusConnectionPrivate *conn = d->connectionPrivate(); - if (conn) - conn->connectRelay(d->service, d->currentOwner, d->path, d->interface, + if (conn) { + // do we know what our owner is? + QString owner; + if (!d->service.isEmpty() && d->currentOwner.isNull()) + owner = QLatin1String(""); + else + owner = d->currentOwner; + conn->connectRelay(d->service, owner, d->path, d->interface, this, signal); + } } /*! diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 9e41708..8fff8b3 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -1487,7 +1487,7 @@ void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage //qDBusDebug() << signalHooks.keys(); for ( ; it != end && it.key() == key; ++it) { const SignalHook &hook = it.value(); - if (!hook.owner.isEmpty() && hook.owner != msg.service()) + if (!hook.owner.isNull() && hook.owner != msg.service()) continue; if (!hook.path.isEmpty() && hook.path != msg.path()) continue; -- cgit v0.12 From ed75146e45d41ca52b64193acbf433e6d2ceaaf5 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 19:33:38 +0200 Subject: Move the bulk of the signal connecting/disconnecting code to QDBusConnectionPrivate I'll need to recurse into the signal connection mechanism in the next commit. --- src/dbus/qdbusconnection.cpp | 63 ++------------------------------------ src/dbus/qdbusconnection_p.h | 6 ++++ src/dbus/qdbusintegrator.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 60 deletions(-) diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index 3aaba68..71e433e 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -619,37 +619,10 @@ bool QDBusConnection::connect(const QString &service, const QString &path, const if (interface.isEmpty() && name.isEmpty()) return false; - // check the slot - QDBusConnectionPrivate::SignalHook hook; - QString key; - QString name2 = name; - if (name2.isNull()) - name2.detach(); - QString owner = d->getNameOwner(service); // we don't care if the owner is empty - hook.signature = signature; // it might get started later - if (!d->prepareHook(hook, key, service, owner, path, interface, name, argumentMatch, receiver, slot, 0, false)) - return false; // don't connect - - // avoid duplicating: + // it might get started later QDBusWriteLocker locker(ConnectAction, d); - QDBusConnectionPrivate::SignalHookHash::ConstIterator it = d->signalHooks.find(key); - QDBusConnectionPrivate::SignalHookHash::ConstIterator end = d->signalHooks.constEnd(); - for ( ; it != end && it.key() == key; ++it) { - const QDBusConnectionPrivate::SignalHook &entry = it.value(); - if (entry.service == hook.service && - entry.owner == hook.owner && - entry.path == hook.path && - entry.signature == hook.signature && - entry.obj == hook.obj && - entry.midx == hook.midx) { - // no need to compare the parameters if it's the same slot - return true; // already there - } - } - - d->connectSignal(key, hook); - return true; + return d->connectSignal(service, owner, path, interface, name, argumentMatch, signature, receiver, slot); } /*! @@ -704,38 +677,8 @@ bool QDBusConnection::disconnect(const QString &service, const QString &path, co if (interface.isEmpty() && name.isEmpty()) return false; - // check the slot - QDBusConnectionPrivate::SignalHook hook; - QString key; - QString name2 = name; - if (name2.isNull()) - name2.detach(); - - QString owner = d->getNameOwner(service); // we don't care of owner is empty - hook.signature = signature; - if (!d->prepareHook(hook, key, service, owner, path, interface, name, argumentMatch, receiver, slot, 0, false)) - return false; // don't disconnect - - // avoid duplicating: QDBusWriteLocker locker(DisconnectAction, d); - QDBusConnectionPrivate::SignalHookHash::Iterator it = d->signalHooks.find(key); - QDBusConnectionPrivate::SignalHookHash::Iterator end = d->signalHooks.end(); - for ( ; it != end && it.key() == key; ++it) { - const QDBusConnectionPrivate::SignalHook &entry = it.value(); - if (entry.service == hook.service && - entry.owner == hook.owner && - entry.path == hook.path && - entry.signature == hook.signature && - entry.obj == hook.obj && - entry.midx == hook.midx) { - // no need to compare the parameters if it's the same slot - d->disconnectSignal(it); - return true; // it was there - } - } - - // the slot was not found - return false; + return d->disconnectSignal(service, path, interface, name, argumentMatch, signature, receiver, slot); } /*! diff --git a/src/dbus/qdbusconnection_p.h b/src/dbus/qdbusconnection_p.h index df51c27..2402719 100644 --- a/src/dbus/qdbusconnection_p.h +++ b/src/dbus/qdbusconnection_p.h @@ -175,8 +175,14 @@ public: QDBusPendingCallPrivate *sendWithReplyAsync(const QDBusMessage &message, int timeout = -1); int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, const char *returnMethod, const char *errorMethod, int timeout = -1); + bool connectSignal(const QString &service, const QString &owner, const QString &path, const QString& interface, + const QString &name, const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot); void connectSignal(const QString &key, const SignalHook &hook); SignalHookHash::Iterator disconnectSignal(SignalHookHash::Iterator &it); + bool disconnectSignal(const QString &service, const QString &path, const QString& interface, + const QString &name, const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot); void registerObject(const ObjectTreeNode *node); void connectRelay(const QString &service, const QString ¤tOwner, const QString &path, const QString &interface, diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 8fff8b3..89a7449 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -1942,6 +1942,42 @@ int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObj return 1; } +bool QDBusConnectionPrivate::connectSignal(const QString &service, const QString &owner, + const QString &path, const QString &interface, const QString &name, + const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot) +{ + // check the slot + QDBusConnectionPrivate::SignalHook hook; + QString key; + QString name2 = name; + if (name2.isNull()) + name2.detach(); + + hook.signature = signature; + if (!prepareHook(hook, key, service, owner, path, interface, name, argumentMatch, receiver, slot, 0, false)) + return false; // don't connect + + // avoid duplicating: + QDBusConnectionPrivate::SignalHookHash::ConstIterator it = signalHooks.find(key); + QDBusConnectionPrivate::SignalHookHash::ConstIterator end = signalHooks.constEnd(); + for ( ; it != end && it.key() == key; ++it) { + const QDBusConnectionPrivate::SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.owner == hook.owner && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // no need to compare the parameters if it's the same slot + return true; // already there + } + } + + connectSignal(key, hook); + return true; +} + void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook) { signalHooks.insertMulti(key, hook); @@ -1973,6 +2009,43 @@ void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook } } +bool QDBusConnectionPrivate::disconnectSignal(const QString &service, + const QString &path, const QString &interface, const QString &name, + const QStringList &argumentMatch, const QString &signature, + QObject *receiver, const char *slot) +{ + // check the slot + QDBusConnectionPrivate::SignalHook hook; + QString key; + QString name2 = name; + if (name2.isNull()) + name2.detach(); + + hook.signature = signature; + if (!prepareHook(hook, key, service, QString(), path, interface, name, argumentMatch, receiver, slot, 0, false)) + return false; // don't disconnect + + // avoid duplicating: + QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key); + QDBusConnectionPrivate::SignalHookHash::Iterator end = signalHooks.end(); + for ( ; it != end && it.key() == key; ++it) { + const QDBusConnectionPrivate::SignalHook &entry = it.value(); + if (entry.service == hook.service && + //entry.owner == hook.owner && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // no need to compare the parameters if it's the same slot + disconnectSignal(it); + return true; // it was there + } + } + + // the slot was not found + return false; +} + QDBusConnectionPrivate::SignalHookHash::Iterator QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) { -- cgit v0.12 From 1176ecf0b533279e5a1c97f183e5c5f1c57fb188 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 19:35:19 +0200 Subject: Stop using the NameOwnerChanged signal without arg0 in QtDBus itself We were using this signal to update the signal hooks when the remote service changed. That meant each Qt app received every single service creation, change or destruction. Now we only watch the services we're really interested in. --- src/dbus/qdbusconnection.cpp | 4 ---- src/dbus/qdbusconnection_p.h | 2 ++ src/dbus/qdbusintegrator.cpp | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp index 71e433e..d7088ff 100644 --- a/src/dbus/qdbusconnection.cpp +++ b/src/dbus/qdbusconnection.cpp @@ -1005,14 +1005,10 @@ void QDBusConnectionPrivate::setBusService(const QDBusConnection &connection) busService = new QDBusConnectionInterface(connection, this); ref.deref(); // busService has increased the refcounting to us // avoid cyclic refcounting -// if (mode != PeerMode) - QObject::connect(busService, SIGNAL(serviceOwnerChanged(QString,QString,QString)), - this, SIGNAL(serviceOwnerChanged(QString,QString,QString))); QObject::connect(this, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), busService, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), Qt::QueuedConnection); - } /*! diff --git a/src/dbus/qdbusconnection_p.h b/src/dbus/qdbusconnection_p.h index 2402719..ed29e4e 100644 --- a/src/dbus/qdbusconnection_p.h +++ b/src/dbus/qdbusconnection_p.h @@ -154,6 +154,7 @@ public: typedef QMultiHash SignalHookHash; typedef QHash MetaObjectHash; typedef QHash MatchRefCountHash; + typedef QHash WatchedServicesHash; public: // public methods are entry points from other objects @@ -270,6 +271,7 @@ public: QDBusError lastError; QStringList serviceNames; + WatchedServicesHash watchedServiceNames; SignalHookHash signalHooks; MatchRefCountHash matchRefCounts; ObjectTreeNode rootNode; diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index 89a7449..c7538c3 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -501,6 +501,11 @@ static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *ro return 0; } +static bool shouldWatchService(const QString &service) +{ + return !service.isEmpty() && !service.startsWith(QLatin1Char(':')); +} + extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook); void qDBusAddSpyHook(QDBusSpyHook hook) { @@ -941,6 +946,7 @@ QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) QDBusMetaTypeId::init(); rootNode.flags = 0; + watchedServiceNames[QLatin1String(DBUS_SERVICE_DBUS)] = 1; connect(this, SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); @@ -2005,6 +2011,22 @@ void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook hook.obj->metaObject()->method(hook.midx).signature(), qPrintable(qerror.name()), qPrintable(qerror.message())); Q_ASSERT(false); + } else { + // Successfully connected the signal + // Do we need to watch for this name? + if (shouldWatchService(hook.service)) { + WatchedServicesHash::Iterator it = watchedServiceNames.find(hook.service); + if (it != watchedServiceNames.end()) { + // already watching + ++it.value(); + } else { + // we need to watch for this service changing + QString dbusServerService = QLatin1String(DBUS_SERVICE_DBUS); + connectSignal(dbusServerService, dbusServerService, QString(), QLatin1String(DBUS_INTERFACE_DBUS), + QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(), + this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); + } + } } } } @@ -2051,6 +2073,19 @@ QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) { const SignalHook &hook = it.value(); + WatchedServicesHash::Iterator sit = watchedServiceNames.find(hook.service); + if (sit != watchedServiceNames.end()) { + if (sit.value() == 1) { + watchedServiceNames.erase(sit); + QString dbusServerService = QLatin1String(DBUS_SERVICE_DBUS); + disconnectSignal(dbusServerService, QString(), QLatin1String(DBUS_INTERFACE_DBUS), + QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(), + this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); + } else { + --sit.value(); + } + } + bool erase = false; MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule); if (i == matchRefCounts.end()) { -- cgit v0.12 From da9a3a919326e35730b13d0660bb4a7b64e2c81c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 23 Oct 2009 19:37:16 +0200 Subject: Add a warning to user's connecting to serviceOwnerChanged directly We want people to not use this signal directly. --- src/dbus/qdbusconnectioninterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/dbus/qdbusconnectioninterface.cpp b/src/dbus/qdbusconnectioninterface.cpp index 8670ed5..414d318 100644 --- a/src/dbus/qdbusconnectioninterface.cpp +++ b/src/dbus/qdbusconnectioninterface.cpp @@ -336,8 +336,14 @@ void QDBusConnectionInterface::connectNotify(const char *signalName) else if (qstrcmp(signalName, SIGNAL(serviceUnregistered(QString))) == 0) QDBusAbstractInterface::connectNotify(SIGNAL(NameLost(QString))); - else if (qstrcmp(signalName, SIGNAL(serviceOwnerChanged(QString,QString,QString))) == 0) + else if (qstrcmp(signalName, SIGNAL(serviceOwnerChanged(QString,QString,QString))) == 0) { + static bool warningPrinted = false; + if (!warningPrinted) { + qWarning("Connecting to deprecated signal QDBusConnectionInterface::serviceOwnerChanged(QString,QString,QString)"); + warningPrinted = true; + } QDBusAbstractInterface::connectNotify(SIGNAL(NameOwnerChanged(QString,QString,QString))); + } } /*! -- cgit v0.12 From fb941d5c297e3205d421126c38a9f3a262584b88 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 26 Oct 2009 12:21:29 +0100 Subject: Add QDBusServiceWatcher class. --- src/dbus/dbus.pro | 148 ++++++++++++++------------- src/dbus/qdbusservicewatcher.cpp | 211 +++++++++++++++++++++++++++++++++++++++ src/dbus/qdbusservicewatcher.h | 103 +++++++++++++++++++ 3 files changed, 393 insertions(+), 69 deletions(-) create mode 100644 src/dbus/qdbusservicewatcher.cpp create mode 100644 src/dbus/qdbusservicewatcher.h diff --git a/src/dbus/dbus.pro b/src/dbus/dbus.pro index 57c6a58..9ab3920 100644 --- a/src/dbus/dbus.pro +++ b/src/dbus/dbus.pro @@ -1,77 +1,87 @@ -TARGET = QtDBus -QPRO_PWD = $$PWD -QT = core xml -CONFIG += link_pkgconfig -DEFINES += QDBUS_MAKEDLL DBUS_API_SUBJECT_TO_CHANGE +TARGET = QtDBus +QPRO_PWD = $$PWD +QT = core \ + xml +CONFIG += link_pkgconfig +DEFINES += QDBUS_MAKEDLL \ + DBUS_API_SUBJECT_TO_CHANGE QMAKE_CXXFLAGS += $$QT_CFLAGS_DBUS - -contains(QT_CONFIG, dbus-linked) { +contains(QT_CONFIG, dbus-linked) { LIBS_PRIVATE += $$QT_LIBS_DBUS - DEFINES += QT_LINKED_LIBDBUS + DEFINES += QT_LINKED_LIBDBUS } -#INCLUDEPATH += . - -unix { - QMAKE_PKGCONFIG_DESCRIPTION = Qt DBus module - QMAKE_PKGCONFIG_REQUIRES = QtCore QtXml +# INCLUDEPATH += . +unix { + QMAKE_PKGCONFIG_DESCRIPTION = Qt \ + DBus \ + module + QMAKE_PKGCONFIG_REQUIRES = QtCore \ + QtXml } - -win32 { - LIBS_PRIVATE += -lws2_32 -ladvapi32 -lnetapi32 -luser32 - CONFIG(debug, debug|release):LIBS_PRIVATE += -ldbus-1d - else:LIBS_PRIVATE += -ldbus-1 +win32 { + LIBS_PRIVATE += -lws2_32 \ + -ladvapi32 \ + -lnetapi32 \ + -luser32 + CONFIG(debug, debug|release):LIBS_PRIVATE += -ldbus-1d + else:LIBS_PRIVATE += -ldbus-1 } - include(../qbase.pri) - -PUB_HEADERS = qdbusargument.h \ - qdbusconnectioninterface.h \ - qdbusmacros.h \ - qdbuserror.h \ - qdbusextratypes.h \ - qdbusmessage.h \ - qdbusserver.h \ - qdbusconnection.h \ - qdbusabstractinterface.h \ - qdbusinterface.h \ - qdbusabstractadaptor.h \ - qdbusreply.h \ - qdbusmetatype.h \ - qdbuspendingcall.h \ - qdbuspendingreply.h \ - qdbuscontext.h - +PUB_HEADERS = qdbusargument.h \ + qdbusconnectioninterface.h \ + qdbusmacros.h \ + qdbuserror.h \ + qdbusextratypes.h \ + qdbusmessage.h \ + qdbusserver.h \ + qdbusconnection.h \ + qdbusabstractinterface.h \ + qdbusinterface.h \ + qdbusabstractadaptor.h \ + qdbusreply.h \ + qdbusmetatype.h \ + qdbuspendingcall.h \ + qdbuspendingreply.h \ + qdbuscontext.h HEADERS += $$PUB_HEADERS \ - qdbusconnection_p.h qdbusmessage_p.h \ - qdbusinterface_p.h qdbusxmlparser_p.h qdbusabstractadaptor_p.h \ - qdbusargument_p.h qdbusutil_p.h qdbusabstractinterface_p.h \ - qdbuscontext_p.h qdbusthreaddebug_p.h qdbusintegrator_p.h \ - qdbuspendingcall_p.h qdbus_symbols_p.h - -SOURCES += qdbusconnection.cpp \ - qdbusconnectioninterface.cpp \ - qdbuserror.cpp \ - qdbusintegrator.cpp \ - qdbusmessage.cpp \ - qdbusserver.cpp \ - qdbusabstractinterface.cpp \ - qdbusinterface.cpp \ - qdbusxmlparser.cpp \ - qdbusutil.cpp \ - qdbusintrospection.cpp \ - qdbusabstractadaptor.cpp \ - qdbusinternalfilters.cpp \ - qdbusmetaobject.cpp \ - qdbusxmlgenerator.cpp \ - qdbusmisc.cpp \ - qdbusargument.cpp \ - qdbusreply.cpp \ - qdbusmetatype.cpp \ - qdbusextratypes.cpp \ - qdbusmarshaller.cpp \ - qdbuscontext.cpp \ - qdbuspendingcall.cpp \ - qdbuspendingreply.cpp \ - qdbus_symbols.cpp - + qdbusconnection_p.h \ + qdbusmessage_p.h \ + qdbusinterface_p.h \ + qdbusxmlparser_p.h \ + qdbusabstractadaptor_p.h \ + qdbusargument_p.h \ + qdbusutil_p.h \ + qdbusabstractinterface_p.h \ + qdbuscontext_p.h \ + qdbusthreaddebug_p.h \ + qdbusintegrator_p.h \ + qdbuspendingcall_p.h \ + qdbus_symbols_p.h \ + qdbusservicewatcher.h +SOURCES += qdbusconnection.cpp \ + qdbusconnectioninterface.cpp \ + qdbuserror.cpp \ + qdbusintegrator.cpp \ + qdbusmessage.cpp \ + qdbusserver.cpp \ + qdbusabstractinterface.cpp \ + qdbusinterface.cpp \ + qdbusxmlparser.cpp \ + qdbusutil.cpp \ + qdbusintrospection.cpp \ + qdbusabstractadaptor.cpp \ + qdbusinternalfilters.cpp \ + qdbusmetaobject.cpp \ + qdbusxmlgenerator.cpp \ + qdbusmisc.cpp \ + qdbusargument.cpp \ + qdbusreply.cpp \ + qdbusmetatype.cpp \ + qdbusextratypes.cpp \ + qdbusmarshaller.cpp \ + qdbuscontext.cpp \ + qdbuspendingcall.cpp \ + qdbuspendingreply.cpp \ + qdbus_symbols.cpp \ + qdbusservicewatcher.cpp diff --git a/src/dbus/qdbusservicewatcher.cpp b/src/dbus/qdbusservicewatcher.cpp new file mode 100644 index 0000000..d8a33c3 --- /dev/null +++ b/src/dbus/qdbusservicewatcher.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusservicewatcher.h" +#include "qdbusconnection.h" +#include "qdbus_symbols_p.h" + +#include + +#include + +Q_GLOBAL_STATIC_WITH_ARGS(QString, busService, (QLatin1String(DBUS_SERVICE_DBUS))) +Q_GLOBAL_STATIC_WITH_ARGS(QString, busPath, (QLatin1String(DBUS_PATH_DBUS))) +Q_GLOBAL_STATIC_WITH_ARGS(QString, busInterface, (QLatin1String(DBUS_INTERFACE_DBUS))) +Q_GLOBAL_STATIC_WITH_ARGS(QString, signalName, (QLatin1String("NameOwnerChanged"))) + +class QDBusServiceWatcherPrivate: public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDBusServiceWatcher) +public: + QDBusServiceWatcherPrivate(const QDBusConnection &c, QDBusServiceWatcher::WatchMode wm) + : connection(c), watchMode(wm) + { + } + + QStringList servicesWatched; + QDBusConnection connection; + QDBusServiceWatcher::WatchMode watchMode; + + void _q_serviceOwnerChanged(const QString &, const QString &, const QString &); + void setConnection(const QStringList &services, const QDBusConnection &c, QDBusServiceWatcher::WatchMode watchMode); + + QStringList matchArgsForService(const QString &service); + void addService(const QString &service); + void removeService(const QString &service); +}; + +void QDBusServiceWatcherPrivate::_q_serviceOwnerChanged(const QString &service, const QString &oldOwner, const QString &newOwner) +{ + Q_Q(QDBusServiceWatcher); + emit q->serviceOwnerChanged(service, oldOwner, newOwner); + if (oldOwner.isEmpty()) + emit q->serviceRegistered(service); + else if (newOwner.isEmpty()) + emit q->serviceUnregistered(service); +} + +void QDBusServiceWatcherPrivate::setConnection(const QStringList &s, const QDBusConnection &c, QDBusServiceWatcher::WatchMode wm) +{ + if (connection.isConnected()) { + // remove older rules + foreach (const QString &s, servicesWatched) + removeService(s); + } + + connection = c; + watchMode = wm; + servicesWatched = s; + + if (connection.isConnected()) { + // add new rules + foreach (const QString &s, servicesWatched) + addService(s); + } +} + +QStringList QDBusServiceWatcherPrivate::matchArgsForService(const QString &service) +{ + QStringList matchArgs; + matchArgs << service; + + switch (watchMode) { + case QDBusServiceWatcher::WatchForOwnerChange: + break; + + case QDBusServiceWatcher::WatchForRegistration: + matchArgs << QString::fromLatin1("", 0); + break; + + case QDBusServiceWatcher::WatchForUnregistration: + matchArgs << QString() << QString::fromLatin1("", 0); + break; + } + return matchArgs; +} + +void QDBusServiceWatcherPrivate::addService(const QString &service) +{ + QStringList matchArgs = matchArgsForService(service); + connection.connect(*busService(), *busPath(), *busInterface(), *signalName(), + matchArgs, QString(), q_func(), + SLOT(_q_serviceOwnerChanged(QString,QString,QString))); +} + +void QDBusServiceWatcherPrivate::removeService(const QString &service) +{ + QStringList matchArgs = matchArgsForService(service); + connection.disconnect(*busService(), *busPath(), *busInterface(), *signalName(), + matchArgs, QString(), q_func(), + SLOT(_q_serviceOwnerChanged(QString,QString,QString))); +} + +QDBusServiceWatcher::QDBusServiceWatcher(QObject *parent) + : QObject(*new QDBusServiceWatcherPrivate(QDBusConnection(QString()), WatchForOwnerChange), parent) +{ +} + +QDBusServiceWatcher::QDBusServiceWatcher(const QString &service, const QDBusConnection &connection, WatchMode watchMode, QObject *parent) + : QObject(*new QDBusServiceWatcherPrivate(connection, watchMode), parent) +{ + d_func()->setConnection(QStringList() << service, connection, watchMode); +} + +QDBusServiceWatcher::~QDBusServiceWatcher() +{ +} + +QStringList QDBusServiceWatcher::watchedServices() const +{ + return d_func()->servicesWatched; +} + +void QDBusServiceWatcher::setWatchedServices(const QStringList &services) +{ + Q_D(QDBusServiceWatcher); + if (services == d->servicesWatched) + return; + d->setConnection(services, d->connection, d->watchMode); +} + +void QDBusServiceWatcher::addWatchedService(const QString &newService) +{ + Q_D(QDBusServiceWatcher); + if (d->servicesWatched.contains(newService)) + return; + d->addService(newService); + d->servicesWatched << newService; +} + +bool QDBusServiceWatcher::removeWatchedService(const QString &service) +{ + Q_D(QDBusServiceWatcher); + d->removeService(service); + return d->servicesWatched.removeOne(service); +} + +QDBusServiceWatcher::WatchMode QDBusServiceWatcher::watchMode() const +{ + return d_func()->watchMode; +} + +void QDBusServiceWatcher::setWatchMode(WatchMode mode) +{ + Q_D(QDBusServiceWatcher); + if (mode == d->watchMode) + return; + d->setConnection(d->servicesWatched, d->connection, mode); +} + +QDBusConnection QDBusServiceWatcher::connection() const +{ + return d_func()->connection; +} + +void QDBusServiceWatcher::setConnection(const QDBusConnection &connection) +{ + Q_D(QDBusServiceWatcher); + if (connection.name() == d->connection.name()) + return; + d->setConnection(d->servicesWatched, connection, d->watchMode); +} + +#include "moc_qdbusservicewatcher.cpp" diff --git a/src/dbus/qdbusservicewatcher.h b/src/dbus/qdbusservicewatcher.h new file mode 100644 index 0000000..a968a9c --- /dev/null +++ b/src/dbus/qdbusservicewatcher.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSSERVICEWATCHER_H +#define QDBUSSERVICEWATCHER_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusConnection; + +class QDBusServiceWatcherPrivate; +class QDBUS_EXPORT QDBusServiceWatcher: public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList watchedServices READ watchedServices WRITE setWatchedServices) + Q_PROPERTY(WatchMode watchMode READ watchMode WRITE setWatchMode) +public: + enum WatchModeFlag { + WatchForRegistration = 0x01, + WatchForUnregistration = 0x02, + WatchForOwnerChange = 0x03 + }; + Q_DECLARE_FLAGS(WatchMode, WatchModeFlag) + + explicit QDBusServiceWatcher(QObject *parent = 0); + QDBusServiceWatcher(const QString &service, const QDBusConnection &connection, + WatchMode watchMode = WatchForOwnerChange, QObject *parent = 0); + ~QDBusServiceWatcher(); + + QStringList watchedServices() const; + void setWatchedServices(const QStringList &services); + void addWatchedService(const QString &newService); + bool removeWatchedService(const QString &service); + + WatchMode watchMode() const; + void setWatchMode(WatchMode mode); + + QDBusConnection connection() const; + void setConnection(const QDBusConnection &connection); + +Q_SIGNALS: + void serviceRegistered(const QString &service); + void serviceUnregistered(const QString &service); + void serviceOwnerChanged(const QString &service, const QString &oldOwner, const QString &newOwner); + +private: + Q_PRIVATE_SLOT(d_func(), void _q_serviceOwnerChanged(QString,QString,QString)) + Q_DISABLE_COPY(QDBusServiceWatcher) + Q_DECLARE_PRIVATE(QDBusServiceWatcher) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusServiceWatcher::WatchMode) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDBUSSERVICEWATCHER_H -- cgit v0.12 From f9b55c5263ba25e707c8cc35988935a804af2f50 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 26 Oct 2009 14:08:01 +0100 Subject: Autotest: add unit test for QDBusServiceWatcher --- .../qdbusservicewatcher/qdbusservicewatcher.pro | 8 + .../tst_qdbusservicewatcher.cpp | 273 +++++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 tests/auto/qdbusservicewatcher/qdbusservicewatcher.pro create mode 100644 tests/auto/qdbusservicewatcher/tst_qdbusservicewatcher.cpp diff --git a/tests/auto/qdbusservicewatcher/qdbusservicewatcher.pro b/tests/auto/qdbusservicewatcher/qdbusservicewatcher.pro new file mode 100644 index 0000000..4970f16 --- /dev/null +++ b/tests/auto/qdbusservicewatcher/qdbusservicewatcher.pro @@ -0,0 +1,8 @@ +load(qttest_p4) +QT = core +contains(QT_CONFIG,dbus): { + SOURCES += tst_qdbusservicewatcher.cpp + QT += dbus +} else { + SOURCES += ../qdbusmarshall/dummy.cpp +} diff --git a/tests/auto/qdbusservicewatcher/tst_qdbusservicewatcher.cpp b/tests/auto/qdbusservicewatcher/tst_qdbusservicewatcher.cpp new file mode 100644 index 0000000..10b43b1 --- /dev/null +++ b/tests/auto/qdbusservicewatcher/tst_qdbusservicewatcher.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +class tst_QDBusServiceWatcher: public QObject +{ + Q_OBJECT + QString serviceName; +public: + tst_QDBusServiceWatcher(); + +private slots: + void initTestCase(); + void cleanup(); + + void watchForCreation(); + void watchForDisappearance(); + void watchForOwnerChange(); + void modeChange(); +}; + +tst_QDBusServiceWatcher::tst_QDBusServiceWatcher() + : serviceName("com.example.TestName") +{ +} + +void tst_QDBusServiceWatcher::initTestCase() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); +} + +void tst_QDBusServiceWatcher::cleanup() +{ + // ensure that the name isn't registered + QDBusConnection::sessionBus().unregisterService(serviceName); +} + +void tst_QDBusServiceWatcher::watchForCreation() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForRegistration); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceRegistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); + + spyR.clear(); + spyU.clear(); + spyO.clear(); + + // unregister it: + con.unregisterService(serviceName); + + // and register again + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); +} + +void tst_QDBusServiceWatcher::watchForDisappearance() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForUnregistration); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceUnregistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + // unregister it: + con.unregisterService(serviceName); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 0); + + QCOMPARE(spyU.count(), 1); + QCOMPARE(spyU.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QCOMPARE(spyO.at(0).at(1).toString(), con.baseService()); + QVERIFY(spyO.at(0).at(2).toString().isEmpty()); +} + +void tst_QDBusServiceWatcher::watchForOwnerChange() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForOwnerChange); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceRegistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); + + spyR.clear(); + spyU.clear(); + spyO.clear(); + + // unregister it: + con.unregisterService(serviceName); + + // and register again + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 1); + QCOMPARE(spyU.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyO.count(), 2); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QCOMPARE(spyO.at(0).at(1).toString(), con.baseService()); + QVERIFY(spyO.at(0).at(2).toString().isEmpty()); + QCOMPARE(spyO.at(1).at(0).toString(), serviceName); + QVERIFY(spyO.at(1).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(1).at(2).toString(), con.baseService()); +} + +void tst_QDBusServiceWatcher::modeChange() +{ + QDBusConnection con = QDBusConnection::sessionBus(); + QVERIFY(con.isConnected()); + + QDBusServiceWatcher watcher(serviceName, con, QDBusServiceWatcher::WatchForRegistration); + + QSignalSpy spyR(&watcher, SIGNAL(serviceRegistered(QString))); + QSignalSpy spyU(&watcher, SIGNAL(serviceUnregistered(QString))); + QSignalSpy spyO(&watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceRegistered(QString)), SLOT(exitLoop())); + + // register a name + QVERIFY(con.registerService(serviceName)); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 1); + QCOMPARE(spyR.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyU.count(), 0); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QVERIFY(spyO.at(0).at(1).toString().isEmpty()); + QCOMPARE(spyO.at(0).at(2).toString(), con.baseService()); + + spyR.clear(); + spyU.clear(); + spyO.clear(); + + watcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); + + // unregister it: + con.unregisterService(serviceName); + + QTestEventLoop::instance().connect(&watcher, SIGNAL(serviceUnregistered(QString)), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(spyR.count(), 0); + + QCOMPARE(spyU.count(), 1); + QCOMPARE(spyU.at(0).at(0).toString(), serviceName); + + QCOMPARE(spyO.count(), 1); + QCOMPARE(spyO.at(0).at(0).toString(), serviceName); + QCOMPARE(spyO.at(0).at(1).toString(), con.baseService()); + QVERIFY(spyO.at(0).at(2).toString().isEmpty()); +} + +QTEST_MAIN(tst_QDBusServiceWatcher) +#include "tst_qdbusservicewatcher.moc" -- cgit v0.12 From bd9a8091eb8c731569e4972a04f23c9a2f48391c Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 26 Oct 2009 15:16:47 +0100 Subject: Doc: add documentation for QDBusServiceWatcher class. --- src/dbus/qdbusservicewatcher.cpp | 163 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/src/dbus/qdbusservicewatcher.cpp b/src/dbus/qdbusservicewatcher.cpp index d8a33c3..4872732 100644 --- a/src/dbus/qdbusservicewatcher.cpp +++ b/src/dbus/qdbusservicewatcher.cpp @@ -138,26 +138,159 @@ void QDBusServiceWatcherPrivate::removeService(const QString &service) SLOT(_q_serviceOwnerChanged(QString,QString,QString))); } +/*! + \class QDBusServiceWatcher + \since 4.6 + \inmodule QtDBus + + \brief The QDBusServiceWatcher class allows the user to watch for a bus service change. + + A QDBusServiceWatcher object can be used to notify the application about + an ownership change of a service name on the bus. It has three watch + modes: + + \list + \o watching for service registration only + \o watching for service unregistration only + \o watching for any kind of service ownership change (the default mode) + \endlist + + Besides being created or deleted, services may change owners without a + unregister/register operation happening. So the \ref serviceRegistered() + and \ref serviceUnregistered() signals may not be emitted if that + happens. + + This class is more efficient than using the + QDBusConnectionInterface::serviceOwnerChanged() signal because it allows + one to receive only the signals for which the class is interested in. + + \sa QDBusConnection +*/ + +/*! + \enum QDBusServiceWatcher::WatchModeFlag + + QDBusServiceWatcher supports three different watch modes, which are configured by this flag: + + \value WatchForRegistration watch for service registration only, ignoring + any signals related to other service ownership change. + + \value WatchForUnregistration watch for service unregistration only, + ignoring any signals related to other service ownership change. + + \value WatchForOwnerChange watch for any kind of service ownership + change. +*/ + +/*! + \property QDBusServiceWatcher::watchMode + + The \c watchMode property holds the current watch mode for this + QDBusServiceWatcher object. The default value for this property is + QDBusServiceWatcher::WatchForOwnershipChange. +*/ + +/*! + \property QDBusServiceWatcher::watchedServices + + The \c servicesWatched property holds the list of services watched. + + Note that modifying this list with setServicesWatched() is an expensive + operation. If you can, prefer to change it by way of addWatchedService() + and removeWatchedService(). +*/ + +/*! + \fn void QDBusServiceWatcher::serviceRegistered(const QString &serviceName) + + This signal is emitted whenever this object detects that the service \a + serviceName became available on the bus. + + \sa serviceUnregistered(), serviceOwnerChanged() +*/ + +/*! + \fn void QDBusServiceWatcher::serviceUnregistered(const QString &serviceName) + + This signal is emitted whenever this object detects that the service \a + serviceName was unregistered from the bus and is no longer available. + + \sa serviceRegistered(), serviceOwnerChanged() +*/ + +/*! + \fn void QDBusServiceWatcher::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) + + This signal is emitted whenever this object detects that there was a + service ownership change relating to the \a serviceName service. The \a + oldOwner parameter contains the old owner name and \a newOwner is the new + owner. Both \a oldOwner and \a newOwner are unique connection names. + + Note that this signal is also emitted whenever the \a serviceName service + was registered or unregistered. If it was registered, \a oldOwner will + contain an empty string, whereas if it was unregistered, \a newOwner will + contain an empty string. + + If you need only to find out if the service is registered or unregistered + only, without being notified that the ownership changed, consider using + the specific modes for those operations. This class is more efficient if + you use the more specific modes. + + \sa serviceRegistered(), serviceUnregistered() +*/ + +/*! + Creates a QDBusServiceWatcher object. Note that until you set a + connection with setConnection(), this object will not emit any signals. + + The \a parent parameter is passed to QObject to set the parent of this + object. +*/ QDBusServiceWatcher::QDBusServiceWatcher(QObject *parent) : QObject(*new QDBusServiceWatcherPrivate(QDBusConnection(QString()), WatchForOwnerChange), parent) { } +/*! + Creates a QDBusServiceWatcher object and attaches it to the \a connection + connection. Also, this function immediately starts watching for \a + watchMode changes to service \a service. + + The \a parent parameter is passed to QObject to set the parent of this + object. +*/ QDBusServiceWatcher::QDBusServiceWatcher(const QString &service, const QDBusConnection &connection, WatchMode watchMode, QObject *parent) : QObject(*new QDBusServiceWatcherPrivate(connection, watchMode), parent) { d_func()->setConnection(QStringList() << service, connection, watchMode); } +/*! + Destroys the QDBusServiceWatcher object and releases any resources + associated with it. +*/ QDBusServiceWatcher::~QDBusServiceWatcher() { } +/*! + Returns the list of D-Bus services that are being watched. + + \sa setWatchedServices() +*/ QStringList QDBusServiceWatcher::watchedServices() const { return d_func()->servicesWatched; } +/*! + Sets the list of D-Bus services being watched to be \a services. + + Note that setting the entire list means removing all previous rules for + watching services and adding new ones. This is an expensive operation and + should be avoided, if possible. Instead, use addWatchedService() and + removeWatchedService() if you can to manipulate entries in the list. +*/ void QDBusServiceWatcher::setWatchedServices(const QStringList &services) { Q_D(QDBusServiceWatcher); @@ -166,6 +299,11 @@ void QDBusServiceWatcher::setWatchedServices(const QStringList &services) d->setConnection(services, d->connection, d->watchMode); } +/*! + Adds \a newService to the list of services to be watched by this object. + This function is more efficient than setWatchedServices() and should be + used whenever possible to add services. +*/ void QDBusServiceWatcher::addWatchedService(const QString &newService) { Q_D(QDBusServiceWatcher); @@ -175,6 +313,14 @@ void QDBusServiceWatcher::addWatchedService(const QString &newService) d->servicesWatched << newService; } +/*! + Removes the \a service from the list of services being watched by this + object. Note that D-Bus notifications are asynchronous, so there may + still be signals pending delivery about \a service. Those signals will + still be emitted whenever the D-Bus messages are processed. + + This function returns true if any services were removed. +*/ bool QDBusServiceWatcher::removeWatchedService(const QString &service) { Q_D(QDBusServiceWatcher); @@ -195,11 +341,28 @@ void QDBusServiceWatcher::setWatchMode(WatchMode mode) d->setConnection(d->servicesWatched, d->connection, mode); } +/*! + Returns the QDBusConnection that this object is attached to. + + \sa setConnection() +*/ QDBusConnection QDBusServiceWatcher::connection() const { return d_func()->connection; } +/*! + Sets the D-Bus connection that this object is attached to be \a + connection. All services watched will be transferred to this connection. + + Note that QDBusConnection objects are reference counted: + QDBusServiceWatcher will keep a reference for this connection while it + exists. The connection is not closed until the reference count drops to + zero, so this will ensure that any notifications are received while this + QDBusServiceWatcher object exists. + + \sa connection() +*/ void QDBusServiceWatcher::setConnection(const QDBusConnection &connection) { Q_D(QDBusServiceWatcher); -- cgit v0.12 From 0283ca4cf2ad1c9e642b24f8376aa7179fa54599 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 22 Oct 2009 20:59:07 +0200 Subject: Add some STL compatibility for QContiguousCache and private inheritance The private inheritance ensures that we don't try to access the types under the wrong pointer. If we did that, we'd cause strict aliasing violations. Reviewed-by: Olivier Goffart --- src/corelib/tools/qcontiguouscache.h | 125 ++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 60 deletions(-) diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index ad78d1f..862df61 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -62,6 +62,12 @@ struct Q_CORE_EXPORT QContiguousCacheData int start; int offset; uint sharable : 1; + // uint unused : 31; + + // total is 24 bytes (HP-UX aCC: 40 bytes) + // the next entry is already aligned to 8 bytes + // there will be an 8 byte gap here if T requires 16-byte alignment + // (such as long double on 64-bit platforms, __int128, __float128) #ifdef QT_QCONTIGUOUSCACHE_DEBUG void dump() const; @@ -69,33 +75,32 @@ struct Q_CORE_EXPORT QContiguousCacheData }; template -struct QContiguousCacheTypedData +struct QContiguousCacheTypedData: private QContiguousCacheData { - QBasicAtomicInt ref; - int alloc; - int count; - int start; - int offset; - uint sharable : 1; - // uint unused : 31; - - // total is 24 bytes (HP-UX aCC: 40 bytes) - // the next entry is already aligned to 8 bytes - // there will be an 8 byte gap here if T requires 16-byte alignment - // (such as long double on 64-bit platforms, __int128, __float128) - + // private inheritance to avoid aliasing warningss T array[1]; + + static inline void free(QContiguousCacheTypedData *data) { QContiguousCacheData::free(data); } }; template class QContiguousCache { typedef QContiguousCacheTypedData Data; - union { QContiguousCacheData *p; QContiguousCacheTypedData *d; }; + union { QContiguousCacheData *d; QContiguousCacheTypedData *p; }; public: + // STL compatibility + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef ptrdiff_t difference_type; + typedef int size_type; + explicit QContiguousCache(int capacity = 0); QContiguousCache(const QContiguousCache &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } - inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) free(d); } + inline ~QContiguousCache() { if (!d) return; if (!d->ref.deref()) free(p); } inline void detach() { if (d->ref != 1) detach_helper(); } inline bool isDetached() const { return d->ref == 1; } @@ -128,10 +133,10 @@ public: inline int firstIndex() const { return d->offset; } inline int lastIndex() const { return d->offset + d->count - 1; } - inline const T &first() const { Q_ASSERT(!isEmpty()); return d->array[d->start]; } - inline const T &last() const { Q_ASSERT(!isEmpty()); return d->array[(d->start + d->count -1) % d->alloc]; } - inline T &first() { Q_ASSERT(!isEmpty()); detach(); return d->array[d->start]; } - inline T &last() { Q_ASSERT(!isEmpty()); detach(); return d->array[(d->start + d->count -1) % d->alloc]; } + inline const T &first() const { Q_ASSERT(!isEmpty()); return p->array[d->start]; } + inline const T &last() const { Q_ASSERT(!isEmpty()); return p->array[(d->start + d->count -1) % d->alloc]; } + inline T &first() { Q_ASSERT(!isEmpty()); detach(); return p->array[d->start]; } + inline T &last() { Q_ASSERT(!isEmpty()); detach(); return p->array[(d->start + d->count -1) % d->alloc]; } void removeFirst(); T takeFirst(); @@ -161,9 +166,9 @@ private: template void QContiguousCache::detach_helper() { - union { QContiguousCacheData *p; QContiguousCacheTypedData *d; } x; + union { QContiguousCacheData *d; QContiguousCacheTypedData *p; } x; - x.p = malloc(d->alloc); + x.d = malloc(d->alloc); x.d->ref = 1; x.d->count = d->count; x.d->start = d->start; @@ -171,8 +176,8 @@ void QContiguousCache::detach_helper() x.d->alloc = d->alloc; x.d->sharable = true; - T *dest = x.d->array + x.d->start; - T *src = d->array + d->start; + T *dest = x.p->array + x.d->start; + T *src = p->array + d->start; int oldcount = x.d->count; while (oldcount--) { if (QTypeInfo::isComplex) { @@ -181,15 +186,15 @@ void QContiguousCache::detach_helper() *dest = *src; } dest++; - if (dest == x.d->array + x.d->alloc) - dest = x.d->array; + if (dest == x.p->array + x.d->alloc) + dest = x.p->array; src++; - if (src == d->array + d->alloc) - src = d->array; + if (src == p->array + d->alloc) + src = p->array; } if (!d->ref.deref()) - free(d); + free(p); d = x.d; } @@ -205,8 +210,8 @@ void QContiguousCache::setCapacity(int asize) x.d->count = qMin(d->count, asize); x.d->offset = d->offset + d->count - x.d->count; x.d->start = x.d->offset % x.d->alloc; - T *dest = x.d->array + (x.d->start + x.d->count-1) % x.d->alloc; - T *src = d->array + (d->start + d->count-1) % d->alloc; + T *dest = x.p->array + (x.d->start + x.d->count-1) % x.d->alloc; + T *src = p->array + (d->start + d->count-1) % d->alloc; int oldcount = x.d->count; while (oldcount--) { if (QTypeInfo::isComplex) { @@ -214,11 +219,11 @@ void QContiguousCache::setCapacity(int asize) } else { *dest = *src; } - if (dest == x.d->array) - dest = x.d->array + x.d->alloc; + if (dest == x.p->array) + dest = x.p->array + x.d->alloc; dest--; - if (src == d->array) - src = d->array + d->alloc; + if (src == p->array) + src = p->array + d->alloc; src--; } /* free old */ @@ -232,24 +237,24 @@ void QContiguousCache::clear() if (d->ref == 1) { if (QTypeInfo::isComplex) { int oldcount = d->count; - T * i = d->array + d->start; - T * e = d->array + d->alloc; + T * i = p->array + d->start; + T * e = p->array + d->alloc; while (oldcount--) { i->~T(); i++; if (i == e) - i = d->array; + i = p->array; } } d->count = d->start = d->offset = 0; } else { - union { QContiguousCacheData *p; QContiguousCacheTypedData *d; } x; - x.p = malloc(d->alloc); + union { QContiguousCacheData *d; QContiguousCacheTypedData *p; } x; + x.d = malloc(d->alloc); x.d->ref = 1; x.d->alloc = d->alloc; x.d->count = x.d->start = x.d->offset = 0; x.d->sharable = true; - if (!d->ref.deref()) free(d); + if (!d->ref.deref()) free(p); d = x.d; } } @@ -263,7 +268,7 @@ inline QContiguousCacheData *QContiguousCache::malloc(int aalloc) template QContiguousCache::QContiguousCache(int cap) { - p = malloc(cap); + d = malloc(cap); d->ref = 1; d->alloc = cap; d->count = d->start = d->offset = 0; @@ -303,13 +308,13 @@ void QContiguousCache::free(Data *x) { if (QTypeInfo::isComplex) { int oldcount = d->count; - T * i = d->array + d->start; - T * e = d->array + d->alloc; + T * i = p->array + d->start; + T * e = p->array + d->alloc; while (oldcount--) { i->~T(); i++; if (i == e) - i = d->array; + i = p->array; } } qFree(x); @@ -320,10 +325,10 @@ void QContiguousCache::append(const T &value) detach(); if (QTypeInfo::isComplex) { if (d->count == d->alloc) - (d->array + (d->start+d->count) % d->alloc)->~T(); - new (d->array + (d->start+d->count) % d->alloc) T(value); + (p->array + (d->start+d->count) % d->alloc)->~T(); + new (p->array + (d->start+d->count) % d->alloc) T(value); } else { - d->array[(d->start+d->count) % d->alloc] = value; + p->array[(d->start+d->count) % d->alloc] = value; } if (d->count == d->alloc) { @@ -349,12 +354,12 @@ void QContiguousCache::prepend(const T &value) d->count++; else if (d->count == d->alloc) - (d->array + d->start)->~T(); + (p->array + d->start)->~T(); if (QTypeInfo::isComplex) - new (d->array + d->start) T(value); + new (p->array + d->start) T(value); else - d->array[d->start] = value; + p->array[d->start] = value; } template @@ -364,9 +369,9 @@ void QContiguousCache::insert(int pos, const T &value) detach(); if (containsIndex(pos)) { if(QTypeInfo::isComplex) - new (d->array + pos % d->alloc) T(value); + new (p->array + pos % d->alloc) T(value); else - d->array[pos % d->alloc] = value; + p->array[pos % d->alloc] = value; } else if (pos == d->offset-1) prepend(value); else if (pos == d->offset+d->count) @@ -378,18 +383,18 @@ void QContiguousCache::insert(int pos, const T &value) d->start = pos % d->alloc; d->count = 1; if (QTypeInfo::isComplex) - new (d->array + d->start) T(value); + new (p->array + d->start) T(value); else - d->array[d->start] = value; + p->array[d->start] = value; } } template inline const T &QContiguousCache::at(int pos) const -{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache::at", "index out of range"); return d->array[pos % d->alloc]; } +{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache::at", "index out of range"); return p->array[pos % d->alloc]; } template inline const T &QContiguousCache::operator[](int pos) const -{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache::at", "index out of range"); return d->array[pos % d->alloc]; } +{ Q_ASSERT_X(pos >= d->offset && pos - d->offset < d->count, "QContiguousCache::at", "index out of range"); return p->array[pos % d->alloc]; } template inline T &QContiguousCache::operator[](int pos) @@ -397,7 +402,7 @@ inline T &QContiguousCache::operator[](int pos) detach(); if (!containsIndex(pos)) insert(pos, T()); - return d->array[pos % d->alloc]; + return p->array[pos % d->alloc]; } template @@ -407,7 +412,7 @@ inline void QContiguousCache::removeFirst() detach(); d->count--; if (QTypeInfo::isComplex) - (d->array + d->start)->~T(); + (p->array + d->start)->~T(); d->start = (d->start + 1) % d->alloc; d->offset++; } @@ -419,7 +424,7 @@ inline void QContiguousCache::removeLast() detach(); d->count--; if (QTypeInfo::isComplex) - (d->array + (d->start + d->count) % d->alloc)->~T(); + (p->array + (d->start + d->count) % d->alloc)->~T(); } template -- cgit v0.12 From 3f0b969772cf3056ed54349bfe6837d9de2995ea Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 22 Oct 2009 18:57:53 +0200 Subject: Add the aligned versions of qMalloc/qRealloc/qFree --- src/corelib/global/qglobal.h | 3 ++ src/corelib/global/qmalloc.cpp | 100 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 5ee1815..cbb8fda 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -2065,6 +2065,9 @@ Q_DECLARE_TYPEINFO(long double, Q_PRIMITIVE_TYPE); Q_CORE_EXPORT void *qMalloc(size_t size); Q_CORE_EXPORT void qFree(void *ptr); Q_CORE_EXPORT void *qRealloc(void *ptr, size_t size); +Q_CORE_EXPORT void *qMallocAligned(size_t size, size_t alignment); +Q_CORE_EXPORT void *qReallocAligned(void *ptr, size_t size, size_t oldsize, size_t alignment); +Q_CORE_EXPORT void qFreeAligned(void *ptr); Q_CORE_EXPORT void *qMemCopy(void *dest, const void *src, size_t n); Q_CORE_EXPORT void *qMemSet(void *dest, int c, size_t n); diff --git a/src/corelib/global/qmalloc.cpp b/src/corelib/global/qmalloc.cpp index a9f103c..db5e519 100644 --- a/src/corelib/global/qmalloc.cpp +++ b/src/corelib/global/qmalloc.cpp @@ -42,6 +42,7 @@ #include "qplatformdefs.h" #include +#include /* Define the container allocation functions in a separate file, so that our @@ -65,4 +66,103 @@ void *qRealloc(void *ptr, size_t size) return ::realloc(ptr, size); } +#if ((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)) +# define HAVE_POSIX_MEMALIGN +#endif + +void *qMallocAligned(size_t size, size_t alignment) +{ +#if defined(Q_OS_WIN) + return _aligned_malloc(size, alignment); +#elif defined(HAVE_POSIX_MEMALIGN) + if (alignment <= sizeof(void*)) + return qMalloc(size); + + // we have posix_memalign + void *ptr = 0; + if (posix_memalign(&ptr, alignment, size) == 0) + return ptr; + return 0; +#else + return qReallocAligned(0, size, 0, alignment); +#endif +} + +void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t alignment) +{ +#if defined(Q_OS_WIN) + Q_UNUSED(oldsize); + return _aligned_realloc(oldptr, newsize, alignment); +#elif defined(HAVE_POSIX_MEMALIGN) + if (alignment <= sizeof(void*)) + return qRealloc(oldptr, newsize); + + void *newptr = qMallocAligned(newsize, alignment); + if (!newptr) + return 0; + qMemCopy(newptr, oldptr, qMin(oldsize, newsize)); + qFree(oldptr); + return newptr; +#else + // fake an aligned allocation + Q_UNUSED(oldsize); + + void *actualptr = oldptr ? static_cast(oldptr)[-1] : 0; + if (alignment <= sizeof(void*)) { + // special, fast case + void **newptr = static_cast(qRealloc(actualptr, newsize + sizeof(void*))); + if (!newptr) + return 0; + if (newptr == actualptr) { + // realloc succeeded without reallocating + return oldptr; + } + + *newptr = newptr; + return newptr + 1; + } + + union { void *ptr; void **pptr; quintptr n; } real, faked; + + // qMalloc returns pointers aligned at least at sizeof(size_t) boundaries + // but usually more (8- or 16-byte boundaries). + // So we overallocate by alignment-sizeof(size_t) bytes, so we're guaranteed to find a + // somewhere within the first alignment-sizeof(size_t) that is properly aligned. + + // However, we need to store the actual pointer, so we need to allocate actually size + + // alignment anyway. + + real.ptr = qRealloc(actualptr, newsize + alignment); + if (!real.ptr) + return 0; + + faked.n = real.n + alignment; + faked.n &= ~(alignment - 1); + + // now save the value of the real pointer at faked-sizeof(void*) + // by construction, alignment > sizeof(void*) and is a power of 2, so + // faked-sizeof(void*) is properly aligned for a pointer + faked.pptr[-1] = real.ptr; + + // and save the actual size just before the pointer + //reinterpret_cast(faked.pptr - 1)[-1] = size; + + return faked.ptr; +#endif +} + +void qFreeAligned(void *ptr) +{ +#if defined(Q_OS_WIN) + _aligned_free(ptr); +#elif defined(HAVE_POSIX_MEMALIGN) + ::free(ptr); +#else + if (!ptr) + return; + void **ptr2 = static_cast(ptr); + free(ptr2[-1]); +#endif +} + QT_END_NAMESPACE -- cgit v0.12 From e83bb2fdfc2dc899526c8157fd8b77a68cdde9da Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 22 Oct 2009 21:09:24 +0200 Subject: Fix Qt containers to properly support types with strict alignments. QContiguousCache is a new type, so there are no binary compatibility issues. QHash and QMap didn't have any public qMalloc / qFree, so the entire logic is contained in .cpp code. However, since old code will not inform us of the alignment requirements, we need to add a bit to the structure to indicate whether strict alignment is in use or not. QList doesn't require any changes. For small, movable types, they're all stored in the pointer array itself, so they're aligned. For larger types, we use new(), so types with stricter requirements should define their own operator new(). QLinkedList cannot be fixed. It uses new() on the QLinkedListNode, which contains a T type. Sorry. QVector did have public qMalloc / qFree. I've moved the calls to the inner function and made it keep the old calls if the alignment requirement is below a certain threshold. The idea is that, if it's above, no one was using QVector anyway. Reviewed-by: Bradley T. Hughes --- src/corelib/global/qmalloc.cpp | 3 - src/corelib/tools/qcontiguouscache.cpp | 10 +++ src/corelib/tools/qcontiguouscache.h | 15 +++- src/corelib/tools/qhash.cpp | 27 ++++--- src/corelib/tools/qhash.h | 36 ++++++---- src/corelib/tools/qmap.cpp | 31 ++++++-- src/corelib/tools/qmap.h | 20 ++++-- src/corelib/tools/qvector.cpp | 27 +++++++ src/corelib/tools/qvector.h | 20 +++++- tests/auto/collections/tst_collections.cpp | 109 +++++++++++++++++++++++++++++ 10 files changed, 259 insertions(+), 39 deletions(-) diff --git a/src/corelib/global/qmalloc.cpp b/src/corelib/global/qmalloc.cpp index db5e519..e33f77c 100644 --- a/src/corelib/global/qmalloc.cpp +++ b/src/corelib/global/qmalloc.cpp @@ -144,9 +144,6 @@ void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t align // faked-sizeof(void*) is properly aligned for a pointer faked.pptr[-1] = real.ptr; - // and save the actual size just before the pointer - //reinterpret_cast(faked.pptr - 1)[-1] = size; - return faked.ptr; #endif } diff --git a/src/corelib/tools/qcontiguouscache.cpp b/src/corelib/tools/qcontiguouscache.cpp index b0ed701..dd7cab6 100644 --- a/src/corelib/tools/qcontiguouscache.cpp +++ b/src/corelib/tools/qcontiguouscache.cpp @@ -56,6 +56,16 @@ void QContiguousCacheData::dump() const } #endif +QContiguousCacheData *QContiguousCacheData::allocate(int size, int alignment) +{ + return static_cast(qMallocAligned(size, alignment)); +} + +void QContiguousCacheData::free(QContiguousCacheData *data) +{ + qFreeAligned(data); +} + /*! \class QContiguousCache \brief The QContiguousCache class is a template class that provides a contiguous cache. \ingroup tools diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index 862df61..b9d04b8 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -69,6 +69,9 @@ struct Q_CORE_EXPORT QContiguousCacheData // there will be an 8 byte gap here if T requires 16-byte alignment // (such as long double on 64-bit platforms, __int128, __float128) + static QContiguousCacheData *allocate(int size, int alignment); + static void free(QContiguousCacheData *data); + #ifdef QT_QCONTIGUOUSCACHE_DEBUG void dump() const; #endif @@ -161,6 +164,14 @@ private: // count the padding at the end return reinterpret_cast(&(reinterpret_cast(this))->array[1]) - reinterpret_cast(this); } + int alignOfTypedData() const + { +#ifdef Q_ALIGNOF + return qMax(sizeof(void*), Q_ALIGNOF(Data)); +#else + return 0; +#endif + } }; template @@ -262,7 +273,7 @@ void QContiguousCache::clear() template inline QContiguousCacheData *QContiguousCache::malloc(int aalloc) { - return static_cast(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T))); + return QContiguousCacheData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); } template @@ -317,7 +328,7 @@ void QContiguousCache::free(Data *x) i = p->array; } } - qFree(x); + x->free(x); } template void QContiguousCache::append(const T &value) diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index f33aba9..23fff1c 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -166,29 +166,38 @@ static int countBits(int hint) const int MinNumBits = 4; QHashData QHashData::shared_null = { - 0, 0, Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, MinNumBits, 0, 0, true + 0, 0, Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, MinNumBits, 0, 0, true, false }; void *QHashData::allocateNode() { - void *ptr = qMalloc(nodeSize); + return allocateNode(0); +} + +void *QHashData::allocateNode(int nodeAlign) +{ + void *ptr = strictAlignment ? qMallocAligned(nodeSize, nodeAlign) : qMalloc(nodeSize); Q_CHECK_PTR(ptr); return ptr; } void QHashData::freeNode(void *node) { - qFree(node); + if (strictAlignment) + qFreeAligned(node); + else + qFree(node); } QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize) { - return detach_helper( node_duplicate, 0, nodeSize ); + return detach_helper2( node_duplicate, 0, nodeSize, 0 ); } -QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), - void (*node_delete)(Node *), - int nodeSize) +QHashData *QHashData::detach_helper2(void (*node_duplicate)(Node *, void *), + void (*node_delete)(Node *), + int nodeSize, + int nodeAlign) { union { QHashData *d; @@ -204,6 +213,7 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), d->numBits = numBits; d->numBuckets = numBuckets; d->sharable = true; + d->strictAlignment = nodeAlign > 8; if (numBuckets) { QT_TRY { @@ -222,7 +232,7 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), Node *oldNode = buckets[i]; while (oldNode != this_e) { QT_TRY { - Node *dup = static_cast(allocateNode()); + Node *dup = static_cast(allocateNode(nodeAlign)); QT_TRY { node_duplicate(oldNode, dup); @@ -262,6 +272,7 @@ void QHashData::free_helper(void (*node_delete)(Node *)) while (cur != this_e) { Node *next = cur->next; node_delete(cur); + freeNode(cur); cur = next; } } diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index b65f1d3..67b394b 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -125,12 +125,14 @@ struct Q_CORE_EXPORT QHashData short numBits; int numBuckets; uint sharable : 1; + uint strictAlignment : 1; - void *allocateNode(); + void *allocateNode(); // ### Qt5 remove me + void *allocateNode(int nodeAlign); void freeNode(void *node); QHashData *detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize); // ### Qt5 remove me - QHashData *detach_helper(void (*node_duplicate)(Node *, void *), void (*node_delete)(Node *), - int nodeSize); + QHashData *detach_helper2(void (*node_duplicate)(Node *, void *), void (*node_delete)(Node *), + int nodeSize, int nodeAlign); void mightGrow(); bool willGrow(); void hasShrunk(); @@ -267,6 +269,14 @@ class QHash return reinterpret_cast(node); } +#ifdef Q_ALIGNOF + static inline int alignOfNode() { return qMax(sizeof(void*), Q_ALIGNOF(Node)); } + static inline int alignOfDummyNode() { return qMax(sizeof(void*), Q_ALIGNOF(DummyNode)); } +#else + static inline int alignOfNode() { return 0; } + static inline int alignOfDummyNode() { return 0; } +#endif + public: inline QHash() : d(&QHashData::shared_null) { d->ref.ref(); } inline QHash(const QHash &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); } @@ -483,7 +493,7 @@ private: Node **findNode(const Key &key, uint *hp = 0) const; Node *createNode(uint h, const Key &key, const T &value, Node **nextNode); void deleteNode(Node *node); - static void deleteNode(QHashData::Node *node); + static void deleteNode2(QHashData::Node *node); static void duplicateNode(QHashData::Node *originalNode, void *newNode); }; @@ -492,12 +502,12 @@ private: template Q_INLINE_TEMPLATE void QHash::deleteNode(Node *node) { - deleteNode(reinterpret_cast(node)); + deleteNode2(reinterpret_cast(node)); + d->freeNode(node); } - template -Q_INLINE_TEMPLATE void QHash::deleteNode(QHashData::Node *node) +Q_INLINE_TEMPLATE void QHash::deleteNode2(QHashData::Node *node) { #ifdef Q_CC_BOR concrete(node)->~QHashNode(); @@ -506,7 +516,6 @@ Q_INLINE_TEMPLATE void QHash::deleteNode(QHashData::Node *node) #else concrete(node)->~Node(); #endif - qFree(node); } template @@ -527,9 +536,9 @@ QHash::createNode(uint ah, const Key &akey, const T &avalue, Node **anex Node *node; if (QTypeInfo::isDummy) { - node = reinterpret_cast(new (d->allocateNode()) DummyNode(akey)); + node = reinterpret_cast(new (d->allocateNode(alignOfDummyNode())) DummyNode(akey)); } else { - node = new (d->allocateNode()) Node(akey, avalue); + node = new (d->allocateNode(alignOfNode())) Node(akey, avalue); } node->h = ah; @@ -554,7 +563,7 @@ Q_INLINE_TEMPLATE QHash &QHash::unite(const QHash &other template Q_OUTOFLINE_TEMPLATE void QHash::freeData(QHashData *x) { - x->free_helper(deleteNode); + x->free_helper(deleteNode2); } template @@ -566,8 +575,9 @@ Q_INLINE_TEMPLATE void QHash::clear() template Q_OUTOFLINE_TEMPLATE void QHash::detach_helper() { - QHashData *x = d->detach_helper(duplicateNode, deleteNode, - QTypeInfo::isDummy ? sizeof(DummyNode) : sizeof(Node)); + QHashData *x = d->detach_helper2(duplicateNode, deleteNode2, + QTypeInfo::isDummy ? sizeof(DummyNode) : sizeof(Node), + QTypeInfo::isDummy ? alignOfDummyNode() : alignOfNode()); if (!d->ref.deref()) freeData(d); d = x; diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 1385810..cfb18b4 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -53,11 +53,16 @@ QT_BEGIN_NAMESPACE QMapData QMapData::shared_null = { &shared_null, { &shared_null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0, false, true + Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0, false, true, false }; QMapData *QMapData::createData() { + return createData(0); +} + +QMapData *QMapData::createData(int alignment) +{ QMapData *d = new QMapData; Q_CHECK_PTR(d); Node *e = reinterpret_cast(d); @@ -69,6 +74,7 @@ QMapData *QMapData::createData() d->randomBits = 0; d->insertInOrder = false; d->sharable = true; + d->strictAlignment = alignment > 8; return d; } @@ -80,11 +86,19 @@ void QMapData::continueFreeData(int offset) while (cur != e) { prev = cur; cur = cur->forward[0]; - qFree(reinterpret_cast(prev) - offset); + if (strictAlignment) + qFreeAligned(reinterpret_cast(prev) - offset); + else + qFree(reinterpret_cast(prev) - offset); } delete this; } +QMapData::Node *QMapData::node_create(Node *update[], int offset) +{ + return node_create(update, offset, 0); +} + /*! Creates a new node inside the data structure. @@ -94,10 +108,12 @@ void QMapData::continueFreeData(int offset) \a offset is an amount of bytes that needs to reserved just before the QMapData::Node structure. + \a alignment dictates the alignment for the data. + \internal \since 4.6 */ -QMapData::Node *QMapData::node_create(Node *update[], int offset) +QMapData::Node *QMapData::node_create(Node *update[], int offset, int alignment) { int level = 0; uint mask = (1 << Sparseness) - 1; @@ -118,7 +134,9 @@ QMapData::Node *QMapData::node_create(Node *update[], int offset) if (level == 3 && !insertInOrder) randomBits = qrand(); - void *concreteNode = qMalloc(offset + sizeof(Node) + level * sizeof(Node *)); + void *concreteNode = strictAlignment ? + qMallocAligned(offset + sizeof(Node) + level * sizeof(Node *), alignment) : + qMalloc(offset + sizeof(Node) + level * sizeof(Node *)); Q_CHECK_PTR(concreteNode); Node *abstractNode = reinterpret_cast(reinterpret_cast(concreteNode) + offset); @@ -145,7 +163,10 @@ void QMapData::node_delete(Node *update[], int offset, Node *node) update[i]->forward[i] = node->forward[i]; } --size; - qFree(reinterpret_cast(node) - offset); + if (strictAlignment) + qFreeAligned(reinterpret_cast(node) - offset); + else + qFree(reinterpret_cast(node) - offset); } #ifdef QT_QMAP_DEBUG diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 688aca6..20980e7 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -74,10 +74,13 @@ struct Q_CORE_EXPORT QMapData uint randomBits; uint insertInOrder : 1; uint sharable : 1; + uint strictAlignment : 1; - static QMapData *createData(); + static QMapData *createData(); // ### Qt5 remove me + static QMapData *createData(int alignment); void continueFreeData(int offset); - Node *node_create(Node *update[], int offset); + Node *node_create(Node *update[], int offset); // ### Qt5 remove me + Node *node_create(Node *update[], int offset, int alignment); void node_delete(Node *update[], int offset, Node *node); #ifdef QT_QMAP_DEBUG uint adjust_ptr(Node *node); @@ -145,6 +148,13 @@ class QMap }; static inline int payload() { return sizeof(PayloadNode) - sizeof(QMapData::Node *); } + static inline int alignment() { +#ifdef Q_ALIGNOF + return qMax(sizeof(void*), Q_ALIGNOF(Node)); +#else + return 0; +#endif + } static inline Node *concrete(QMapData::Node *node) { return reinterpret_cast(reinterpret_cast(node) - payload()); } @@ -414,7 +424,7 @@ template Q_INLINE_TEMPLATE typename QMapData::Node * QMap::node_create(QMapData *adt, QMapData::Node *aupdate[], const Key &akey, const T &avalue) { - QMapData::Node *abstractNode = adt->node_create(aupdate, payload()); + QMapData::Node *abstractNode = adt->node_create(aupdate, payload(), alignment()); QT_TRY { Node *concreteNode = concrete(abstractNode); new (&concreteNode->key) Key(akey); @@ -715,7 +725,7 @@ template Q_OUTOFLINE_TEMPLATE void QMap::detach_helper() { union { QMapData *d; QMapData::Node *e; } x; - x.d = QMapData::createData(); + x.d = QMapData::createData(alignment()); if (d->size) { x.d->insertInOrder = true; QMapData::Node *update[QMapData::LastLevel + 1]; @@ -905,7 +915,7 @@ Q_OUTOFLINE_TEMPLATE bool QMap::operator==(const QMap &other) co template Q_OUTOFLINE_TEMPLATE QMap::QMap(const std::map &other) { - d = QMapData::createData(); + d = QMapData::createData(alignment()); d->insertInOrder = true; typename std::map::const_iterator it = other.end(); while (it != other.begin()) { diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 20f3a80..6522791 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -45,6 +45,13 @@ QT_BEGIN_NAMESPACE +static inline int alignmentThreshold() +{ + // malloc on 32-bit platforms should return pointers that are 8-byte aligned or more + // while on 64-bit platforms they should be 16-byte aligned or more + return 2 * sizeof(void*); +} + QVectorData QVectorData::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, true, false }; QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) @@ -55,6 +62,26 @@ QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVe return p; } +QVectorData *QVectorData::allocate(int size, int alignment) +{ + return static_cast(alignment > alignmentThreshold() ? qMallocAligned(size, alignment) : qMalloc(size)); +} + +QVectorData *QVectorData::reallocate(QVectorData *x, int newsize, int oldsize, int alignment) +{ + if (alignment > alignmentThreshold()) + return static_cast(qReallocAligned(x, newsize, oldsize, alignment)); + return static_cast(qRealloc(x, newsize)); +} + +void QVectorData::free(QVectorData *x, int alignment) +{ + if (alignment > alignmentThreshold()) + qFreeAligned(x); + else + qFree(x); +} + int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive) { if (excessive) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index b77b53a..cf7df12 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -79,6 +79,9 @@ struct Q_CORE_EXPORT QVectorData // some debugges when the QVector is member of a class within an unnamed namespace. // ### Qt 5: can be removed completely. (Ralf) static QVectorData *malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init); + static QVectorData *allocate(int size, int alignment); + static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment); + static void free(QVectorData *data, int alignment); static int grow(int sizeofTypedData, int size, int sizeofT, bool excessive); }; @@ -87,6 +90,8 @@ struct QVectorTypedData : private QVectorData { // private inheritance as we must not access QVectorData member thought QVectorTypedData // as this would break strict aliasing rules. (in the case of shared_null) T array[1]; + + static inline void free(QVectorTypedData *x, int alignment) { QVectorData::free(x, alignment); } }; class QRegion; @@ -302,6 +307,14 @@ private: // count the padding at the end return reinterpret_cast(&(reinterpret_cast(this))->array[1]) - reinterpret_cast(this); } + inline int alignOfTypedData() const + { +#ifdef Q_ALIGNOF + return qMax(sizeof(void*), Q_ALIGNOF(Data)); +#else + return 0; +#endif + } }; template @@ -373,7 +386,7 @@ QVector &QVector::operator=(const QVector &v) template inline QVectorData *QVector::malloc(int aalloc) { - QVectorData *vectordata = static_cast(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T))); + QVectorData *vectordata = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(vectordata); return vectordata; } @@ -420,7 +433,7 @@ void QVector::free(Data *x) while (i-- != b) i->~T(); } - qFree(x); + x->free(x, alignOfTypedData()); } template @@ -459,7 +472,8 @@ void QVector::realloc(int asize, int aalloc) } } else { QT_TRY { - QVectorData *mem = static_cast(qRealloc(p, sizeOfTypedData() + (aalloc - 1) * sizeof(T))); + QVectorData *mem = QVectorData::reallocate(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T), + sizeOfTypedData() + (d->alloc - 1) * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); x.d = d = mem; x.d->size = d->size; diff --git a/tests/auto/collections/tst_collections.cpp b/tests/auto/collections/tst_collections.cpp index 670cff0..f97805e 100644 --- a/tests/auto/collections/tst_collections.cpp +++ b/tests/auto/collections/tst_collections.cpp @@ -164,6 +164,7 @@ private slots: void qtimerList(); void containerTypedefs(); void forwardDeclared(); + void alignment(); }; struct LargeStatic { @@ -3481,5 +3482,113 @@ void tst_Collections::forwardDeclared() { typedef QSet C; C *x = 0; /* C::iterator i; */ C::const_iterator j; Q_UNUSED(x) } } +#if defined(Q_ALIGNOF) && defined(Q_DECL_ALIGN) + +class Q_DECL_ALIGN(4) Aligned4 +{ + char i; +public: + Aligned4(int i = 0) : i(i) {} + bool checkAligned() const + { + return (quintptr(this) & 3) == 0; + } + + inline bool operator==(const Aligned4 &other) const { return i == other.i; } + inline bool operator<(const Aligned4 &other) const { return i < other.i; } + friend inline int qHash(const Aligned4 &a) { return qHash(a.i); } +}; + +class Q_DECL_ALIGN(128) Aligned128 +{ + char i; +public: + Aligned128(int i = 0) : i(i) {} + bool checkAligned() const + { + return (quintptr(this) & 127) == 0; + } + + inline bool operator==(const Aligned128 &other) const { return i == other.i; } + inline bool operator<(const Aligned128 &other) const { return i < other.i; } + friend inline int qHash(const Aligned128 &a) { return qHash(a.i); } +}; + +template +void testVectorAlignment() +{ + typedef typename C::value_type Aligned; + C container; + container.append(Aligned()); + QVERIFY(container[0].checkAligned()); + + for (int i = 0; i < 200; ++i) + container.append(Aligned()); + + for (int i = 0; i < container.size(); ++i) + QVERIFY(container.at(i).checkAligned()); +} + +template +void testContiguousCacheAlignment() +{ + typedef typename C::value_type Aligned; + C container(150); + container.append(Aligned()); + QVERIFY(container[container.firstIndex()].checkAligned()); + + for (int i = 0; i < 200; ++i) + container.append(Aligned()); + + for (int i = container.firstIndex(); i < container.lastIndex(); ++i) + QVERIFY(container.at(i).checkAligned()); +} + +template +void testAssociativeContainerAlignment() +{ + typedef typename C::key_type Key; + typedef typename C::mapped_type Value; + C container; + container.insert(Key(), Value()); + + typename C::const_iterator it = container.constBegin(); + QVERIFY(it.key().checkAligned()); + QVERIFY(it.value().checkAligned()); + + // add some more elements + for (int i = 0; i < 200; ++i) + container.insert(Key(i), Value(i)); + + it = container.constBegin(); + for ( ; it != container.constEnd(); ++it) { + QVERIFY(it.key().checkAligned()); + QVERIFY(it.value().checkAligned()); + } +} + +void tst_Collections::alignment() +{ + testVectorAlignment >(); + testVectorAlignment >(); + testContiguousCacheAlignment >(); + testContiguousCacheAlignment >(); + testAssociativeContainerAlignment >(); + testAssociativeContainerAlignment >(); + testAssociativeContainerAlignment >(); + testAssociativeContainerAlignment >(); + testAssociativeContainerAlignment >(); + testAssociativeContainerAlignment >(); + testAssociativeContainerAlignment >(); + testAssociativeContainerAlignment >(); +} + +#else +void tst_Collections::alignment() +{ + QSKIP("Compiler doesn't support necessary extension keywords", SkipAll) +} +#endif + QTEST_APPLESS_MAIN(tst_Collections) #include "tst_collections.moc" -- cgit v0.12 From 5b4b6b2be7b901ef9a29c37431998034730fa3d3 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 28 Oct 2009 12:47:35 +0100 Subject: Initialise the reserved bits to 0. This is future compatibility: we must rely on them being 0 in older versions of Qt. Reviewed-by: Bradley T. Hughes --- src/corelib/tools/qcontiguouscache.h | 3 ++- src/corelib/tools/qhash.cpp | 3 ++- src/corelib/tools/qhash.h | 1 + src/corelib/tools/qmap.cpp | 3 ++- src/corelib/tools/qmap.h | 1 + src/corelib/tools/qvector.cpp | 2 +- src/corelib/tools/qvector.h | 2 ++ 7 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index b9d04b8..ef5b238 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -62,7 +62,7 @@ struct Q_CORE_EXPORT QContiguousCacheData int start; int offset; uint sharable : 1; - // uint unused : 31; + uint reserved : 31; // total is 24 bytes (HP-UX aCC: 40 bytes) // the next entry is already aligned to 8 bytes @@ -186,6 +186,7 @@ void QContiguousCache::detach_helper() x.d->offset = d->offset; x.d->alloc = d->alloc; x.d->sharable = true; + x.d->reserved = 0; T *dest = x.p->array + x.d->start; T *src = p->array + d->start; diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 23fff1c..c82c389 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -166,7 +166,7 @@ static int countBits(int hint) const int MinNumBits = 4; QHashData QHashData::shared_null = { - 0, 0, Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, MinNumBits, 0, 0, true, false + 0, 0, Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, MinNumBits, 0, 0, true, false, 0 }; void *QHashData::allocateNode() @@ -214,6 +214,7 @@ QHashData *QHashData::detach_helper2(void (*node_duplicate)(Node *, void *), d->numBuckets = numBuckets; d->sharable = true; d->strictAlignment = nodeAlign > 8; + d->reserved = 0; if (numBuckets) { QT_TRY { diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 67b394b..1918229 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -126,6 +126,7 @@ struct Q_CORE_EXPORT QHashData int numBuckets; uint sharable : 1; uint strictAlignment : 1; + uint reserved : 30; void *allocateNode(); // ### Qt5 remove me void *allocateNode(int nodeAlign); diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index cfb18b4..3b48c3f 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE QMapData QMapData::shared_null = { &shared_null, { &shared_null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0, false, true, false + Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0, false, true, false, 0 }; QMapData *QMapData::createData() @@ -75,6 +75,7 @@ QMapData *QMapData::createData(int alignment) d->insertInOrder = false; d->sharable = true; d->strictAlignment = alignment > 8; + d->reserved = 0; return d; } diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 20980e7..65c3d2a 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -75,6 +75,7 @@ struct Q_CORE_EXPORT QMapData uint insertInOrder : 1; uint sharable : 1; uint strictAlignment : 1; + uint reserved : 29; static QMapData *createData(); // ### Qt5 remove me static QMapData *createData(int alignment); diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 6522791..8bb1074 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -52,7 +52,7 @@ static inline int alignmentThreshold() return 2 * sizeof(void*); } -QVectorData QVectorData::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, true, false }; +QVectorData QVectorData::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, true, false, 0 }; QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) { diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index cf7df12..7402d77 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -72,6 +72,7 @@ struct Q_CORE_EXPORT QVectorData #else uint sharable : 1; uint capacity : 1; + uint reserved : 30; #endif static QVectorData shared_null; @@ -486,6 +487,7 @@ void QVector::realloc(int asize, int aalloc) x.d->alloc = aalloc; x.d->sharable = true; x.d->capacity = d->capacity; + x.d->reserved = 0; } if (QTypeInfo::isComplex) { -- cgit v0.12 From 138e8c17767959c183b3e00e3fb364ab5b6fbdfd Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Wed, 28 Oct 2009 14:14:25 +0100 Subject: QLocalServer: block indefinitely when timeout value is -1 ... as described in the documentation. Furthermore: * use qt_safe_select to timeout correctly * return immediately when timeout value is 0 Reviewed-by: Oswald Buddenhagen --- src/network/socket/qlocalserver_unix.cpp | 22 ++++++---------------- tests/auto/qlocalsocket/tst_qlocalsocket.cpp | 2 +- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp index 5ffe0c0..e09e547 100644 --- a/src/network/socket/qlocalserver_unix.cpp +++ b/src/network/socket/qlocalserver_unix.cpp @@ -216,24 +216,14 @@ void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut) timeout.tv_sec = msec / 1000; timeout.tv_usec = (msec % 1000) * 1000; - // timeout can not be 0 or else select will return an error. - if (0 == msec) - timeout.tv_usec = 1000; - int result = -1; - // on Linux timeout will be updated by select, but _not_ on other systems. - QTime timer; - timer.start(); - while (pendingConnections.isEmpty() && (-1 == msec || timer.elapsed() < msec)) { - result = ::select(listenSocket + 1, &readfds, 0, 0, &timeout); - if (-1 == result && errno != EINTR) { - setError(QLatin1String("QLocalServer::waitForNewConnection")); - closeServer(); - break; - } - if (result > 0) - _q_onNewConnection(); + result = qt_safe_select(listenSocket + 1, &readfds, 0, 0, (msec == -1) ? 0 : &timeout); + if (-1 == result) { + setError(QLatin1String("QLocalServer::waitForNewConnection")); + closeServer(); } + if (result > 0) + _q_onNewConnection(); if (timedOut) *timedOut = (result == 0); } diff --git a/tests/auto/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/qlocalsocket/tst_qlocalsocket.cpp index be39d00..ab7b0ac 100644 --- a/tests/auto/qlocalsocket/tst_qlocalsocket.cpp +++ b/tests/auto/qlocalsocket/tst_qlocalsocket.cpp @@ -976,7 +976,7 @@ void tst_QLocalSocket::writeOnlySocket() #if defined(Q_OS_SYMBIAN) QTest::qWait(250); #endif - QVERIFY(server.waitForNewConnection()); + QVERIFY(server.waitForNewConnection(200)); QLocalSocket* serverSocket = server.nextPendingConnection(); QVERIFY(serverSocket); -- cgit v0.12 From d5a228fbd44f7dd92a10981a5c835db66e3ea6f8 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 29 Oct 2009 09:59:40 +0100 Subject: Fix compilation in QContiguousCache. Reviewed-by: TrustMe --- src/corelib/tools/qcontiguouscache.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index ef5b238..3785938 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -216,8 +216,8 @@ void QContiguousCache::setCapacity(int asize) if (asize == d->alloc) return; detach(); - union { QContiguousCacheData *p; QContiguousCacheTypedData *d; } x; - x.p = malloc(asize); + union { QContiguousCacheData *d; QContiguousCacheTypedData *p; } x; + x.d = malloc(asize); x.d->alloc = asize; x.d->count = qMin(d->count, asize); x.d->offset = d->offset + d->count - x.d->count; @@ -239,7 +239,7 @@ void QContiguousCache::setCapacity(int asize) src--; } /* free old */ - free(d); + free(p); d = x.d; } -- cgit v0.12 From 8307d3511b35adbb945948eda2cf54bbd3c0a20e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 29 Oct 2009 10:28:28 +0100 Subject: Update confusing qWarning message. Reviewed-by: Olivier Goffart --- src/corelib/kernel/qmetaobject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index f98c449..71afc5b 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -574,8 +574,8 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, co if (i >= 0 && m && m->d.superdata) { int conflict = m->d.superdata->indexOfMethod(signal); if (conflict >= 0) - qWarning("QMetaObject::indexOfSignal:%s: Conflict with %s::%s", - m->d.stringdata, m->d.superdata->d.stringdata, signal); + qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", + signal, m->d.superdata->d.stringdata, m->d.stringdata); } #endif return i; -- cgit v0.12 From ffe49ed60c9ee778b9999ee4145b44851b053f9f Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 29 Oct 2009 10:31:48 +0100 Subject: Fix compilation on Mac: there's no malloc.h there Reviewed-by: Trust Me --- src/corelib/global/qmalloc.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/corelib/global/qmalloc.cpp b/src/corelib/global/qmalloc.cpp index e33f77c..3584c50 100644 --- a/src/corelib/global/qmalloc.cpp +++ b/src/corelib/global/qmalloc.cpp @@ -42,7 +42,10 @@ #include "qplatformdefs.h" #include -#include + +#ifdef Q_OS_WIN +# include +#endif /* Define the container allocation functions in a separate file, so that our @@ -66,10 +69,6 @@ void *qRealloc(void *ptr, size_t size) return ::realloc(ptr, size); } -#if ((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600)) -# define HAVE_POSIX_MEMALIGN -#endif - void *qMallocAligned(size_t size, size_t alignment) { #if defined(Q_OS_WIN) -- cgit v0.12 From 9e01ced1d14467c06d7e1db57982556c9e861372 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 29 Oct 2009 18:07:06 +0100 Subject: Remove the posix_memalign and Win32 _aligned_malloc MinGW doesn't have _aligned_malloc and posix_memalign doesn't have a realloc function. So use our own implementation in all platforms. Removing posix_memalign was reviewed by Brad, but I apparently lost the commit. Reviewed-by: Trust Me --- src/corelib/global/qmalloc.cpp | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/src/corelib/global/qmalloc.cpp b/src/corelib/global/qmalloc.cpp index 3584c50..4aab1bd 100644 --- a/src/corelib/global/qmalloc.cpp +++ b/src/corelib/global/qmalloc.cpp @@ -43,10 +43,6 @@ #include -#ifdef Q_OS_WIN -# include -#endif - /* Define the container allocation functions in a separate file, so that our users can easily override them. @@ -71,38 +67,11 @@ void *qRealloc(void *ptr, size_t size) void *qMallocAligned(size_t size, size_t alignment) { -#if defined(Q_OS_WIN) - return _aligned_malloc(size, alignment); -#elif defined(HAVE_POSIX_MEMALIGN) - if (alignment <= sizeof(void*)) - return qMalloc(size); - - // we have posix_memalign - void *ptr = 0; - if (posix_memalign(&ptr, alignment, size) == 0) - return ptr; - return 0; -#else return qReallocAligned(0, size, 0, alignment); -#endif } void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t alignment) { -#if defined(Q_OS_WIN) - Q_UNUSED(oldsize); - return _aligned_realloc(oldptr, newsize, alignment); -#elif defined(HAVE_POSIX_MEMALIGN) - if (alignment <= sizeof(void*)) - return qRealloc(oldptr, newsize); - - void *newptr = qMallocAligned(newsize, alignment); - if (!newptr) - return 0; - qMemCopy(newptr, oldptr, qMin(oldsize, newsize)); - qFree(oldptr); - return newptr; -#else // fake an aligned allocation Q_UNUSED(oldsize); @@ -144,21 +113,14 @@ void *qReallocAligned(void *oldptr, size_t newsize, size_t oldsize, size_t align faked.pptr[-1] = real.ptr; return faked.ptr; -#endif } void qFreeAligned(void *ptr) { -#if defined(Q_OS_WIN) - _aligned_free(ptr); -#elif defined(HAVE_POSIX_MEMALIGN) - ::free(ptr); -#else if (!ptr) return; void **ptr2 = static_cast(ptr); free(ptr2[-1]); -#endif } QT_END_NAMESPACE -- cgit v0.12 From 6f602c01b9dc2b037cb42cacfe1c2df6e092a6b4 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 29 Oct 2009 18:13:21 +0100 Subject: Autotest: this test is failing, enable debugging to find out why. It doesn't happen on my machine, so I need to figure out what's different. Reviewed-by: Trust Me --- tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp b/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp index d84350b..91050f5 100644 --- a/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp +++ b/tests/auto/qdbusabstractinterface/tst_qdbusabstractinterface.cpp @@ -132,6 +132,9 @@ tst_QDBusAbstractInterface::tst_QDBusAbstractInterface() void tst_QDBusAbstractInterface::initTestCase() { + // enable debugging temporarily: + putenv("QDBUS_DEBUG=1"); + // register the object QDBusConnection con = QDBusConnection::sessionBus(); QVERIFY(con.isConnected()); @@ -469,7 +472,7 @@ void tst_QDBusAbstractInterface::followSignal() QVERIFY(!QTestEventLoop::instance().timeout()); // now the signal must have been received: - QVERIFY(s.size() == 1); + QCOMPARE(s.size(), 1); QVERIFY(s.at(0).size() == 0); s.clear(); -- cgit v0.12