Hello community, here is the log from the commit of package akonadi-server for openSUSE:Factory checked in at 2017-03-15 00:50:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/akonadi-server (Old) and /work/SRC/openSUSE:Factory/.akonadi-server.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "akonadi-server" Wed Mar 15 00:50:22 2017 rev:26 rq:478097 version:16.12.3 Changes: -------- --- /work/SRC/openSUSE:Factory/akonadi-server/akonadi-server.changes 2017-02-26 17:08:22.871864525 +0100 +++ /work/SRC/openSUSE:Factory/.akonadi-server.new/akonadi-server.changes 2017-03-15 00:50:22.957941005 +0100 @@ -1,0 +2,28 @@ +Thu Mar 9 18:07:28 UTC 2017 - [email protected] + +- Drop fix-itemretriever-in-case-of-concurrent-requests.patch, it's + already merged upstream + +------------------------------------------------------------------- +Thu Mar 9 12:33:17 CET 2017 - [email protected] + +- Update to 16.12.3 + * New bugfix release + * For more details please see: + * https://www.kde.org/announcements/announce-applications-16.12.3.php +- Changes since 16.12.2: + * Fix ServerManager going into Broken state when called before going to the event loop. + * Fix crash when Connection is closed while ItemRetriever is running (kde#374734) + * DataStream: throw exception if device is null (kde#376385) + * Don't export internal classes when not building autotests. + * Remove unused included cpp file from autotest. + * Fix ItemRetriever in case of concurrent requests for the same item(s) + * no need to generate the header twice, we can use the one from src/server here + * Revert unwanted changes in previous commit + * Implement "TODO: Qt 5: Use QDir::removeRecursively" + * Fix runtime warning at the end of itemretrievertest. + * Show collection id if collection is valid + * Add more debug + + +------------------------------------------------------------------- Old: ---- akonadi-16.12.2.tar.xz fix-itemretriever-in-case-of-concurrent-requests.patch New: ---- akonadi-16.12.3.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ akonadi-server.spec ++++++ --- /var/tmp/diff_new_pack.WhOnA9/_old 2017-03-15 00:50:24.085781418 +0100 +++ /var/tmp/diff_new_pack.WhOnA9/_new 2017-03-15 00:50:24.085781418 +0100 @@ -18,10 +18,10 @@ %define rname akonadi %define kf5_version 5.26.0 -# Latest stable Applications (e.g. 16.08 in KA, but 16.12.2 in KUA) +# Latest stable Applications (e.g. 16.08 in KA, but 16.12.3 in KUA) %{!?_kapp_version: %global _kapp_version %(echo %{version}| awk -F. '{print $1"."$2}')} Name: akonadi-server -Version: 16.12.2 +Version: 16.12.3 Release: 0 Summary: PIM Storage Service License: LGPL-2.1+ @@ -31,8 +31,6 @@ Source99: akonadi-server-rpmlintrc # PATCH-FIX-UPSTREAM handle-mysql-process-crashes-gracefully.patch Patch0: handle-mysql-process-crashes-gracefully.patch -# PATCH-FIX-UPSTREAM fix-itemretriever-in-case-of-concurrent-requests.patch - Fixes hangs when accessing folders -Patch1: fix-itemretriever-in-case-of-concurrent-requests.patch %if 0%{?suse_version} > 1325 BuildRequires: libboost_headers-devel %else @@ -172,7 +170,6 @@ %prep %setup -q -n %{rname}-%{version} %patch0 -p1 -%patch1 -p1 %build %cmake_kf5 -d build -- -DINSTALL_QSQLITE_IN_QT_PREFIX=TRUE -DQT_PLUGINS_DIR=%{_kf5_plugindir} ++++++ akonadi-16.12.2.tar.xz -> akonadi-16.12.3.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/CMakeLists.txt new/akonadi-16.12.3/CMakeLists.txt --- old/akonadi-16.12.2/CMakeLists.txt 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/CMakeLists.txt 2017-03-04 15:02:02.000000000 +0100 @@ -22,7 +22,7 @@ include(AkonadiMacros) -set(PIM_VERSION "5.4.2") +set(PIM_VERSION "5.4.3") set(QT_REQUIRED_VERSION "5.6.0") set(AKONADI_VERSION ${PIM_VERSION}) @@ -79,7 +79,9 @@ add_definitions(-DQT_STRICT_ITERATORS) -set(AKONADI_TESTS_EXPORT AKONADICORE_EXPORT) +if(BUILD_TESTING) + set(AKONADI_TESTS_EXPORT AKONADICORE_EXPORT) +endif() configure_file(akonaditests_export.h.in "${CMAKE_CURRENT_BINARY_DIR}/akonaditests_export.h") # Make sure the KF5Akonadi_DATA_DIR is absolute before passing it to KF5AkonadiConfig.cmake.in diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/autotests/libs/actionstatemanagertest.cpp new/akonadi-16.12.3/autotests/libs/actionstatemanagertest.cpp --- old/akonadi-16.12.2/autotests/libs/actionstatemanagertest.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/autotests/libs/actionstatemanagertest.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -27,8 +27,6 @@ #include "../src/widgets/actionstatemanager.cpp" #undef QT_NO_CLIPBOARD -#include "../src/core/pastehelper.cpp" - typedef QHash<Akonadi::StandardActionManager::Type, bool> StateMap; Q_DECLARE_METATYPE(StateMap) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/autotests/server/CMakeLists.txt new/akonadi-16.12.3/autotests/server/CMakeLists.txt --- old/akonadi-16.12.2/autotests/server/CMakeLists.txt 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/autotests/server/CMakeLists.txt 2017-03-04 15:02:02.000000000 +0100 @@ -34,7 +34,6 @@ dbinitializer.cpp ${CMAKE_CURRENT_BINARY_DIR}/dbpopulator.cpp ) -ecm_qt_declare_logging_category(common_SRCS HEADER akonadiserver_debug.h IDENTIFIER AKONADISERVER_LOG CATEGORY_NAME org.kde.pim.akonadiserver) add_library(akonadi_unittest_common STATIC ${common_SRCS}) target_link_libraries(akonadi_unittest_common diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/autotests/server/fakeakonadiserver.cpp new/akonadi-16.12.3/autotests/server/fakeakonadiserver.cpp --- old/akonadi-16.12.2/autotests/server/fakeakonadiserver.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/autotests/server/fakeakonadiserver.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -243,29 +243,6 @@ return true; } -bool FakeAkonadiServer::deleteDirectory(const QString &path) -{ - // TODO: Qt 5: Use QDir::removeRecursively - - Q_ASSERT(path.startsWith(basePath())); - QDir dir(path); - dir.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden); - - const QFileInfoList list = dir.entryInfoList(); - for (int i = 0; i < list.size(); ++i) { - if (list.at(i).isDir() && !list.at(i).isSymLink()) { - deleteDirectory(list.at(i).absoluteFilePath()); - const QDir tmpDir(list.at(i).absoluteDir()); - tmpDir.rmdir(list.at(i).fileName()); - } else { - QFile::remove(list.at(i).absoluteFilePath()); - } - } - dir.cdUp(); - dir.rmdir(path); - return true; -} - bool FakeAkonadiServer::quit() { qDebug() << "==== Fake Akonadi Server shutting down ===="; @@ -277,8 +254,8 @@ const QCommandLineParser &args = AkApplicationBase::instance()->commandLineArguments(); if (!args.isSet(QLatin1String("no-cleanup"))) { - deleteDirectory(basePath()); - qDebug() << "Cleaned up" << basePath(); + bool ok = QDir(basePath()).removeRecursively(); + qDebug() << "Cleaned up" << basePath() << "success=" << ok; } else { qDebug() << "Skipping clean up of" << basePath(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/autotests/server/fakeakonadiserver.h new/akonadi-16.12.3/autotests/server/fakeakonadiserver.h --- old/akonadi-16.12.2/autotests/server/fakeakonadiserver.h 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/autotests/server/fakeakonadiserver.h 2017-03-04 15:02:02.000000000 +0100 @@ -118,8 +118,6 @@ private: explicit FakeAkonadiServer(); - bool deleteDirectory(const QString &path); - FakeDataStore *mDataStore; FakeSearchManager *mSearchManager; FakeConnection *mConnection; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/autotests/server/itemretrievertest.cpp new/akonadi-16.12.3/autotests/server/itemretrievertest.cpp --- old/akonadi-16.12.2/autotests/server/itemretrievertest.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/autotests/server/itemretrievertest.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -20,6 +20,7 @@ #include <QObject> #include <QTest> #include <QTimer> +#include <QMutex> #include "storage/itemretriever.h" #include "storage/itemretrievaljob.h" @@ -144,6 +145,52 @@ QMultiHash<qint64, JobResult> mJobResults; }; +using RequestedParts = QVector<QByteArray /* FQ name */>; + +class ClientThread : public QThread +{ +public: + ClientThread(Entity::Id itemId, const RequestedParts &requestedParts) + : m_itemId(itemId), m_requestedParts(requestedParts) + {} + + void run() Q_DECL_OVERRIDE + { + // ItemRetriever should... + ItemRetriever retriever; + retriever.setItem(m_itemId); + retriever.setRetrieveParts(m_requestedParts); + QSignalSpy spy(&retriever, &ItemRetriever::itemsRetrieved); + + const bool success = retriever.exec(); + + QMutexLocker lock(&m_mutex); + m_results.success = success; + m_results.signalsCount = spy.count(); + if (m_results.signalsCount > 0) { + m_results.emittedItems = spy.at(0).at(0).value<QList<qint64>>(); + } + } + + struct Results + { + bool success; + int signalsCount; + QList<qint64> emittedItems; + }; + Results results() const { + QMutexLocker lock(&m_mutex); + return m_results; + } + +private: + const Entity::Id m_itemId; + const RequestedParts m_requestedParts; + + mutable QMutex m_mutex; // protects results below + Results m_results; +}; + class ItemRetrieverTest : public QObject { Q_OBJECT @@ -151,7 +198,6 @@ using ExistingParts = QVector<QPair<QByteArray /* name */, QByteArray /* data */>>; using AvailableParts = QVector<QPair<QByteArray /* name */, QByteArray /* data */>>; - using RequestedParts = QVector<QByteArray /* FQ name */>; public: ItemRetrieverTest() @@ -252,63 +298,89 @@ // Setup - DbInitializer dbInitializer; - FakeItemRetrievalJobFactory factory(dbInitializer); - ItemRetrievalManager mgr(&factory); - QTest::qWait(100); - - // Given a PimItem with existing parts - Resource res = dbInitializer.createResource("testresource"); - Collection col = dbInitializer.createCollection("col1"); - PimItem item = dbInitializer.createItem("1", col); - Q_FOREACH (const auto &existingPart, existingParts) { - dbInitializer.createPart(item.id(), existingPart.first, existingPart.second); - } + for (int step = 0; step < 2; ++step) { + DbInitializer dbInitializer; + FakeItemRetrievalJobFactory factory(dbInitializer); + ItemRetrievalManager mgr(&factory); + QTest::qWait(100); + + // Given a PimItem with existing parts + Resource res = dbInitializer.createResource("testresource"); + Collection col = dbInitializer.createCollection("col1"); + + // step 0: do it in the main thread, for easier debugging + PimItem item = dbInitializer.createItem("1", col); + Q_FOREACH (const auto &existingPart, existingParts) { + dbInitializer.createPart(item.id(), existingPart.first, existingPart.second); + } - Q_FOREACH (const auto &availablePart, availableParts) { - factory.addJobResult(item.id(), availablePart.first, availablePart.second); - } + Q_FOREACH (const auto &availablePart, availableParts) { + factory.addJobResult(item.id(), availablePart.first, availablePart.second); + } - // ItemRetriever should... - ItemRetriever retriever; - retriever.setItem(item.id()); - retriever.setRetrieveParts(requestedParts); - QSignalSpy spy(&retriever, &ItemRetriever::itemsRetrieved); + if (step == 0) { + ClientThread thread(item.id(), requestedParts); + thread.run(); + + const ClientThread::Results results = thread.results(); + // ItemRetriever should ... succeed + QVERIFY(results.success); + // Emit exactly one signal ... + QCOMPARE(results.signalsCount, expectedSignals); + // ... with that one item + if (expectedSignals > 0) { + QCOMPARE(results.emittedItems, QList<qint64>{ item.id() }); + } - // Succeed - QVERIFY(retriever.exec()); - // Run exactly one retrieval job - QCOMPARE(factory.jobsCount(), expectedRetrievalJobs); - // Emit exactly one signal ... - QCOMPARE(spy.count(), expectedSignals); - // ... with that one item - if (expectedSignals > 0) { - QCOMPARE(spy.at(0).at(0).value<QList<qint64>>(), QList<qint64>{ item.id() }); - } + // Check that the factory had exactly one retrieval job + QCOMPARE(factory.jobsCount(), expectedRetrievalJobs); - // and the part exists in the DB - const auto parts = item.parts(); - QCOMPARE(parts.count(), expectedParts); - Q_FOREACH (const Part &dbPart, item.parts()) { - const QString fqname = dbPart.partType().ns() + QLatin1Char(':') + dbPart.partType().name(); - if (!requestedParts.contains(fqname.toLatin1())) { - continue; + } else { + QVector<ClientThread *> threads; + for (int i = 0; i < 20; ++i) { + threads.append(new ClientThread(item.id(), requestedParts)); + } + for (int i = 0; i < threads.size(); ++i) { + threads.at(i)->start(); + } + for (int i = 0; i < threads.size(); ++i) { + threads.at(i)->wait(); + } + for (int i = 0; i < threads.size(); ++i) { + const ClientThread::Results results = threads.at(i)->results(); + QVERIFY(results.success); + QCOMPARE(results.signalsCount, expectedSignals); + if (expectedSignals > 0) { + QCOMPARE(results.emittedItems, QList<qint64>{ item.id() }); + } + } + qDeleteAll(threads); } - auto it = std::find_if(availableParts.constBegin(), availableParts.constEnd(), - [dbPart](const QPair<QByteArray, QByteArray> &p) { - return dbPart.partType().name().toLatin1() == p.first; - }); - if (it == availableParts.constEnd()) { - it = std::find_if(existingParts.constBegin(), existingParts.constEnd(), - [fqname](const QPair<QByteArray, QByteArray> &p) { - return fqname.toLatin1() == p.first; - }); - QVERIFY(it != existingParts.constEnd()); - } + // Check that the parts now exist in the DB + const auto parts = item.parts(); + QCOMPARE(parts.count(), expectedParts); + Q_FOREACH (const Part &dbPart, item.parts()) { + const QString fqname = dbPart.partType().ns() + QLatin1Char(':') + dbPart.partType().name(); + if (!requestedParts.contains(fqname.toLatin1())) { + continue; + } + + auto it = std::find_if(availableParts.constBegin(), availableParts.constEnd(), + [dbPart](const QPair<QByteArray, QByteArray> &p) { + return dbPart.partType().name().toLatin1() == p.first; + }); + if (it == availableParts.constEnd()) { + it = std::find_if(existingParts.constBegin(), existingParts.constEnd(), + [fqname](const QPair<QByteArray, QByteArray> &p) { + return fqname.toLatin1() == p.first; + }); + QVERIFY(it != existingParts.constEnd()); + } - QCOMPARE(dbPart.data(), it->second); - QCOMPARE(dbPart.datasize(), it->second.size()); + QCOMPARE(dbPart.data(), it->second); + QCOMPARE(dbPart.datasize(), it->second.size()); + } } } }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/core/agentmanager.cpp new/akonadi-16.12.3/src/core/agentmanager.cpp --- old/akonadi-16.12.2/src/core/agentmanager.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/core/agentmanager.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -383,6 +383,11 @@ AgentType::List AgentManager::types() const { + // Maybe the Control process is up and ready but we haven't been to the event loop yet so serviceOwnerChanged wasn't called yet. + // In that case make sure to do it here, to avoid going into Broken state. + if (d->mTypes.isEmpty()) { + d->readAgentTypes(); + } return Akonadi::valuesToVector(d->mTypes); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/core/jobs/itemfetchjob.cpp new/akonadi-16.12.3/src/core/jobs/itemfetchjob.cpp --- old/akonadi-16.12.2/src/core/jobs/itemfetchjob.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/core/jobs/itemfetchjob.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -89,7 +89,22 @@ } else { try { - return QString(); //QString::fromLatin1(ProtocolHelper::entitySetToScope(mRequestedItems)); + QString itemStr = QStringLiteral("items id: "); + bool firstItem = true; + Q_FOREACH(const Akonadi::Item &item, mRequestedItems) { + if (firstItem) { + firstItem = false; + } else { + itemStr += QStringLiteral(", "); + } + itemStr += QString::number(item.id()); + const Akonadi::Collection parentCollection = item.parentCollection(); + if (parentCollection.isValid()) { + itemStr += QStringLiteral(" from collection %1").arg(parentCollection.id()); + } + } + return itemStr; + //return QString(); //QString::fromLatin1(ProtocolHelper::entitySetToScope(mRequestedItems)); } catch (const Exception &e) { return QString::fromUtf8(e.what()); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/private/datastream_p.cpp new/akonadi-16.12.3/src/private/datastream_p.cpp --- old/akonadi-16.12.2/src/private/datastream_p.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/private/datastream_p.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -59,6 +59,8 @@ void DataStream::waitForData(quint32 size) { + checkDevice(); + while (mDev->bytesAvailable() < size) { if (!mDev->waitForReadyRead(mWaitTimeout)) { throw ProtocolException("Timeout while waiting for data"); @@ -68,7 +70,7 @@ void DataStream::writeRawData(const char *data, int len) { - Q_ASSERT(mDev); + checkDevice(); int ret = mDev->write(data, len); if (ret != len) { @@ -87,6 +89,7 @@ int DataStream::readRawData(char *buffer, int len) { - Q_ASSERT(mDev); + checkDevice(); + return mDev->read(buffer, len); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/private/datastream_p_p.h new/akonadi-16.12.3/src/private/datastream_p_p.h --- old/akonadi-16.12.2/src/private/datastream_p_p.h 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/private/datastream_p_p.h 2017-03-04 15:02:02.000000000 +0100 @@ -75,10 +75,15 @@ void waitForData(quint32 size); private: - - Q_DISABLE_COPY(DataStream) + inline void checkDevice() const + { + if (Q_UNLIKELY(!mDev)) { + throw ProtocolException("Device does not exist"); + } + } + QIODevice *mDev; int mWaitTimeout; }; @@ -87,7 +92,7 @@ inline typename std::enable_if<std::is_integral<T>::value, DataStream>::type &DataStream::operator<<(T val) { - Q_ASSERT(mDev); + checkDevice(); if (mDev->write((char *)&val, sizeof(T)) != sizeof(T)) { throw Akonadi::ProtocolException("Failed to write data to stream"); } @@ -103,7 +108,6 @@ inline DataStream &DataStream::operator<<(const QString &str) { - Q_ASSERT(mDev); if (str.isNull()) { *this << (quint32) 0xffffffff; } else { @@ -114,7 +118,6 @@ inline DataStream &DataStream::operator<<(const QByteArray &data) { - Q_ASSERT(mDev); if (data.isNull()) { *this << (quint32) 0xffffffff; } else { @@ -143,7 +146,8 @@ inline typename std::enable_if<std::is_integral<T>::value, DataStream>::type &DataStream::operator>>(T &val) { - Q_ASSERT(mDev); + checkDevice(); + waitForData(sizeof(T)); if (mDev->read((char *)&val, sizeof(T)) != sizeof(T)) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/server/connection.cpp new/akonadi-16.12.3/src/server/connection.cpp --- old/akonadi-16.12.2/src/server/connection.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/server/connection.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -31,7 +31,8 @@ #include "tracer.h" #include "collectionreferencemanager.h" -#include <assert.h> +#include <cassert> +#include <cxxabi.h> #include <private/protocol_p.h> #include <private/datastream_p_p.h> @@ -53,6 +54,7 @@ , m_verifyCacheOnRetrieval(false) , m_idleTimer(Q_NULLPTR) , m_totalTime( 0 ) + , m_connectionClosing(false) , m_reportTime( false ) { } @@ -107,6 +109,12 @@ void Connection::quit() { + if (QThread::currentThread()->loopLevel() > 1) { + m_connectionClosing = true; + Q_EMIT connectionClosing(); + return; + } + Tracer::self()->endConnection(m_identifier, QString()); collectionReferenceManager()->removeSession(m_sessionId); @@ -255,6 +263,15 @@ if (m_currentHandler) { m_currentHandler->failureResponse(QString::fromUtf8(e.type()) + QLatin1String(": ") + QString::fromUtf8(e.what())); } + } catch (abi::__forced_unwind&) { + // HACK: NPTL throws __forced_unwind during thread cancellation and + // we *must* rethrow it otherwise the program aborts. Due to the issue + // described in #376385 we might end up destroying (cancelling) the + // thread from a nested loop executed inside parseStream() above, + // so the exception raised in there gets caught by this try..catch + // statement and it must be rethrown at all cost. Remove this hack + // once the root problem is fixed. + throw; } catch (...) { qCCritical(AKONADISERVER_LOG) << "Unknown exception caught in Connection for session" << m_sessionId; if (m_currentHandler) { @@ -271,6 +288,12 @@ Q_EMIT disconnected(); return; } + + if (m_connectionClosing) { + m_socket->disconnect(this); + QTimer::singleShot(0, this, &Connection::quit); + return; + } } // reset, arm the timer diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/server/connection.h new/akonadi-16.12.3/src/server/connection.h --- old/akonadi-16.12.2/src/server/connection.h 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/server/connection.h 2017-03-04 15:02:02.000000000 +0100 @@ -77,6 +77,7 @@ Q_SIGNALS: void disconnected(); + void connectionClosing(); protected Q_SLOTS: /** @@ -114,6 +115,8 @@ QHash<QString, qint64> m_totalTimeByHandler; QHash<QString, qint64> m_executionsByHandler; + bool m_connectionClosing; + private: void sendResponse(qint64 tag, const Protocol::Command &response); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/server/storage/datastore.cpp new/akonadi-16.12.3/src/server/storage/datastore.cpp --- old/akonadi-16.12.2/src/server/storage/datastore.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/server/storage/datastore.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -160,6 +160,7 @@ QueryCache::clear(); m_database.close(); m_database = QSqlDatabase(); + m_transactionQueries.clear(); QSqlDatabase::removeDatabase(m_connectionName); m_dbOpened = false; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/server/storage/itemretrievalmanager.cpp new/akonadi-16.12.3/src/server/storage/itemretrievalmanager.cpp --- old/akonadi-16.12.2/src/server/storage/itemretrievalmanager.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/server/storage/itemretrievalmanager.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -232,6 +232,7 @@ qCDebug(AKONADISERVER_LOG) << "someone else requested item" << request->ids << "as well, marking as processed"; (*it)->errorMsg = errorMsg; (*it)->processed = true; + Q_EMIT requestFinished(*it); it = mPendingRequests[request->resourceId].erase(it); } else { ++it; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/server/storage/itemretriever.cpp new/akonadi-16.12.3/src/server/storage/itemretriever.cpp --- old/akonadi-16.12.2/src/server/storage/itemretriever.cpp 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/server/storage/itemretriever.cpp 2017-03-04 15:02:02.000000000 +0100 @@ -317,17 +317,20 @@ QEventLoop eventLoop; connect(ItemRetrievalManager::instance(), &ItemRetrievalManager::requestFinished, this, [&](ItemRetrievalRequest *finishedRequest) { - if (!finishedRequest->errorMsg.isEmpty()) { - mLastError = finishedRequest->errorMsg.toUtf8(); - eventLoop.exit(1); - } else { - requests.removeOne(finishedRequest); - Q_EMIT itemsRetrieved(finishedRequest->ids); - if (requests.isEmpty()) { - eventLoop.quit(); - } + if (requests.removeOne(finishedRequest)) { + if (!finishedRequest->errorMsg.isEmpty()) { + mLastError = finishedRequest->errorMsg.toUtf8(); + eventLoop.exit(1); + } else { + Q_EMIT itemsRetrieved(finishedRequest->ids); + if (requests.isEmpty()) { + eventLoop.quit(); } - }, Qt::UniqueConnection); + } + } + }, Qt::UniqueConnection); + connect(mConnection, &Connection::connectionClosing, + &eventLoop, [&eventLoop]() { eventLoop.exit(1); }); auto it = requests.begin(); while (it != requests.end()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/akonadi-16.12.2/src/shared/aktest.h new/akonadi-16.12.3/src/shared/aktest.h --- old/akonadi-16.12.2/src/shared/aktest.h 2017-01-19 13:12:42.000000000 +0100 +++ new/akonadi-16.12.3/src/shared/aktest.h 2017-03-04 15:02:02.000000000 +0100 @@ -46,6 +46,7 @@ AkCoreApplication app(argc, argv); \ app.addCommandLineOptions(QCommandLineOption( \ QLatin1String("no-cleanup"), QLatin1String("Don't clean up the temporary runtime environment"))); \ + app.parseCommandLine(); \ TestObject tc; \ return QTest::qExec(&tc, argc, argv); \ }
