Skip to content
This repository was archived by the owner on Mar 4, 2023. It is now read-only.

Commit f9e0809

Browse files
committed
fixed cleanup code of engine threads
1 parent b1622f8 commit f9e0809

File tree

8 files changed

+71
-85
lines changed

8 files changed

+71
-85
lines changed

src/datasync/conflictresolver.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "conflictresolver.h"
22
#include "conflictresolver_p.h"
3+
#include "defaults_p.h"
34
using namespace QtDataSync;
45

56
ConflictResolver::ConflictResolver(QObject *parent) :
@@ -11,9 +12,9 @@ ConflictResolver::~ConflictResolver() = default;
1112

1213
void ConflictResolver::setDefaults(const Defaults &defaults)
1314
{
14-
d->defaults = defaults;
15-
d->logger = d->defaults.createLogger(name(), this);
16-
d->settings = d->defaults.createSettings(this, QString::fromUtf8(name()));
15+
d->defaultsName = defaults.setupName();
16+
d->logger = defaults.createLogger(name(), this);
17+
d->settings = defaults.createSettings(this, QString::fromUtf8(name()));
1718
}
1819

1920
QByteArray ConflictResolver::name() const
@@ -23,7 +24,7 @@ QByteArray ConflictResolver::name() const
2324

2425
Defaults ConflictResolver::defaults() const
2526
{
26-
return d->defaults;
27+
return DefaultsPrivate::obtainDefaults(d->defaultsName);
2728
}
2829

2930
Logger *ConflictResolver::logger() const

src/datasync/conflictresolver_p.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace QtDataSync {
1010
class ConflictResolverPrivate
1111
{
1212
public:
13-
Defaults defaults;
13+
QString defaultsName;
1414
Logger *logger = nullptr;
1515
QSettings *settings = nullptr;
1616
};

src/datasync/defaults.cpp

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ void DefaultsPrivate::createDefaults(const QString &setupName, bool isPassive, c
219219
void DefaultsPrivate::removeDefaults(const QString &setupName)
220220
{
221221
QMutexLocker _(&setupDefaultsMutex);
222-
#ifndef QT_NO_DEBUG
223222
QWeakPointer<DefaultsPrivate> weakRef;
224223
{
225224
auto ref = setupDefaults.take(setupName);
@@ -233,30 +232,6 @@ void DefaultsPrivate::removeDefaults(const QString &setupName)
233232
#undef QTDATASYNC_LOG
234233
#define QTDATASYNC_LOG logger
235234
}
236-
#else
237-
setupDefaults.remove(setupName);
238-
#endif
239-
}
240-
241-
void DefaultsPrivate::clearDefaults()
242-
{
243-
QMutexLocker _(&setupDefaultsMutex);
244-
#ifndef QT_NO_DEBUG
245-
QList<QPair<QString, QWeakPointer<DefaultsPrivate>>> weakRefs;
246-
for(auto it = setupDefaults.constBegin(); it != setupDefaults.constEnd(); it++)
247-
weakRefs.append({it.key(), it.value().toWeakRef()});
248-
setupDefaults.clear();
249-
for(const auto &ref : qAsConst(weakRefs)) {
250-
#undef QTDATASYNC_LOG
251-
#define QTDATASYNC_LOG ref.second.toStrongRef()->logger
252-
if(ref.second)
253-
logCritical() << "Defaults for setup still in user after setup was deleted!";
254-
#undef QTDATASYNC_LOG
255-
#define QTDATASYNC_LOG logger
256-
}
257-
#else
258-
setupDefaults.clear();
259-
#endif
260235
}
261236

262237
QSharedPointer<DefaultsPrivate> DefaultsPrivate::obtainDefaults(const QString &setupName)

src/datasync/defaults_p.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class Q_DATASYNC_EXPORT DefaultsPrivate : public QObject
5454
QJsonSerializer *serializer,
5555
ConflictResolver *resolver);
5656
static void removeDefaults(const QString &setupName);
57-
static void clearDefaults();
5857
static QSharedPointer<DefaultsPrivate> obtainDefaults(const QString &setupName);
5958

6059
DefaultsPrivate(QString setupName,

src/datasync/exchangeengine.cpp

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ void ExchangeEngine::finalize()
175175
//remoteconnector is the only one asynchronous (for now)
176176
connect(_remoteConnector, &RemoteConnector::finalized,
177177
thread(), &QThread::quit,
178-
Qt::DirectConnection); //TODO unsafe?
178+
Qt::DirectConnection);
179179

180180
_syncController->finalize();
181181
_changeController->finalize();
@@ -350,16 +350,15 @@ EngineThread::EngineThread(QString setupName, ExchangeEngine *engine, QLockFile
350350
setTerminationEnabled(true);
351351
_engine.load()->moveToThread(this);
352352

353-
connect(thread(), &QThread::finished,
354-
this, &EngineThread::stopSelf);
355-
//TODO dangerous? find better way
356-
// connect(this, &QThread::finished,
357-
// this, &QThread::deleteLater);
353+
connect(this, &QThread::finished,
354+
this, [this](){
355+
_deleteBlocker.clear();
356+
}, Qt::QueuedConnection);
358357
}
359358

360359
EngineThread::~EngineThread()
361360
{
362-
Q_ASSERT_X(!isRunning(), Q_FUNC_INFO, "Engine thread destroyed while engine is still running");
361+
Q_ASSERT_X(isFinished(), Q_FUNC_INFO, "Engine thread destroyed while engine is still running");
363362
}
364363

365364
QString EngineThread::name() const
@@ -372,11 +371,6 @@ ExchangeEngine *EngineThread::engine() const
372371
return _engine.load();
373372
}
374373

375-
bool EngineThread::isRunning() const
376-
{
377-
return _running;
378-
}
379-
380374
bool EngineThread::startEngine()
381375
{
382376
if(!_running.testAndSetRelaxed(false, true))
@@ -396,7 +390,7 @@ bool EngineThread::stopEngine()
396390

397391
void EngineThread::waitAndTerminate(unsigned long timeout)
398392
{
399-
if(!isRunning())
393+
if(isFinished())
400394
return;
401395
if(!wait(timeout)) {
402396
qCWarning(qdssetup) << "Workerthread of setup" << _name << "did not finish before the timout. Terminating...";
@@ -410,13 +404,21 @@ void EngineThread::waitAndTerminate(unsigned long timeout)
410404
void EngineThread::run()
411405
{
412406
try {
407+
// run
413408
qCDebug(qdssetup) << "Engine thread for setup" << _name << "has started";
414409
_engine.load()->initialize();
415410
exec();
411+
412+
// cleanup
416413
_running = false;
417414
delete _engine;
418415
_lockFile->unlock();
419416
delete _lockFile;
417+
DefaultsPrivate::removeDefaults(_name);
418+
419+
// remove from engine list
420+
QMutexLocker _cn(&SetupPrivate::setupMutex);
421+
_deleteBlocker = SetupPrivate::engines.take(_name);
420422
qCDebug(qdssetup) << "Engine thread for setup" << _name << "has stopped";
421423
} catch(QException &e) {
422424
if(_lockFile)
@@ -427,9 +429,3 @@ void EngineThread::run()
427429
qFatal("%s", e.what());
428430
}
429431
}
430-
431-
void EngineThread::stopSelf()
432-
{
433-
stopEngine();
434-
waitAndTerminate(SetupPrivate::currentTimeout());
435-
}

src/datasync/exchangeengine_p.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ class EngineThread : public QThread
129129

130130
QString name() const;
131131
ExchangeEngine *engine() const;
132-
bool isRunning() const;
133132

134133
bool startEngine();
135134
bool stopEngine();
@@ -139,14 +138,14 @@ class EngineThread : public QThread
139138
protected:
140139
void run() override;
141140

142-
private Q_SLOTS:
143-
void stopSelf();
144-
145141
private:
146142
const QString _name;
147143
QAtomicPointer<ExchangeEngine> _engine;
148144
QLockFile *_lockFile;
149145
QAtomicInteger<quint16> _running = false;
146+
147+
//delete blocker
148+
QSharedPointer<EngineThread> _deleteBlocker;
150149
};
151150

152151
}

src/datasync/setup.cpp

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,22 @@ void Setup::setCleanupTimeout(unsigned long timeout)
2626
void Setup::removeSetup(const QString &name, bool waitForFinished)
2727
{
2828
QMutexLocker _(&SetupPrivate::setupMutex);
29+
if(!SetupPrivate::engines.contains(name))
30+
return;
2931
auto mThread = SetupPrivate::engines.value(name);
3032
_.unlock(); //unlock first
3133

3234
if(mThread) {
3335
mThread->stopEngine();
3436
if(waitForFinished)
3537
mThread->waitAndTerminate(SetupPrivate::timeout);
36-
} else //no there -> remove defaults (either already removed does nothing, or remove passive)
38+
} else {
39+
//was set but null -> must be passive -> remove passive defaults
3740
DefaultsPrivate::removeDefaults(name);
41+
// and remove from setup
42+
_.relock();
43+
SetupPrivate::engines.remove(name);
44+
}
3845
}
3946

4047
QStringList Setup::keystoreProviders()
@@ -428,9 +435,6 @@ Setup &Setup::setAccountTrusted(const QByteArray &importData, const QString &pas
428435

429436
void Setup::create(const QString &name)
430437
{
431-
if(QThread::currentThread() != qApp->thread())
432-
qCWarning(qdssetup) << "Setup::create should only be called from the main thread";
433-
434438
QMutexLocker _(&SetupPrivate::setupMutex);
435439
if(SetupPrivate::engines.contains(name))
436440
throw SetupExistsException(name);
@@ -450,27 +454,29 @@ void Setup::create(const QString &name)
450454
if(d->initialImport.isSet())
451455
engine->prepareInitialAccount(d->initialImport);
452456

453-
// create and connect the new thread
454-
auto thread = new EngineThread{name, engine, lockFile};
455-
// once the thread finished, clear the engine from the cache of known ones
456-
QObject::connect(thread, &QThread::finished, [name](){
457-
QMutexLocker _cn(&SetupPrivate::setupMutex);
458-
SetupPrivate::engines.remove(name);
459-
_cn.unlock();
460-
DefaultsPrivate::removeDefaults(name);
461-
}); //queued connection, sent from within the thread to thread object on current thread
462-
463-
// start the thread and cache engine data
457+
// create and start the engine in its thread
458+
QSharedPointer<EngineThread> thread {
459+
new EngineThread{name, engine, lockFile},
460+
&SetupPrivate::deleteThread
461+
};
464462
SetupPrivate::engines.insert(name, thread);
465463
thread->startEngine();
466464
}
467465

468466
bool Setup::createPassive(const QString &name, int timeout)
469467
{
468+
QMutexLocker _(&SetupPrivate::setupMutex);
469+
if(SetupPrivate::engines.contains(name))
470+
throw SetupExistsException(name);
471+
470472
// create storage dir and defaults
471473
auto storageDir = d->createStorageDir(name);
472474
d->createDefaults(name, storageDir, true);
473475

476+
// theoretically "ready" -> save (with null engine) and unlock
477+
SetupPrivate::engines.insert(name, {});
478+
_.unlock();
479+
474480
//wait for the setup to complete initialization
475481
auto defaults = DefaultsPrivate::obtainDefaults(name);
476482
auto isCreated = false;
@@ -501,28 +507,30 @@ bool Setup::createPassive(const QString &name, int timeout)
501507

502508
const QString SetupPrivate::DefaultLocalDir = QStringLiteral("./qtdatasync/default");
503509
QMutex SetupPrivate::setupMutex(QMutex::Recursive);
504-
QHash<QString, QPointer<EngineThread>> SetupPrivate::engines;
510+
QHash<QString, QSharedPointer<EngineThread>> SetupPrivate::engines;
505511
unsigned long SetupPrivate::timeout = ULONG_MAX;
506512

507513
void SetupPrivate::cleanupHandler()
508514
{
509515
QMutexLocker _(&setupMutex);
510-
auto threads = engines.values();
516+
auto threads = engines;
511517
engines.clear();
512518
_.unlock();
513519

514-
for(auto &thread : threads) {
515-
if(thread->stopEngine() &&
516-
thread->thread() != QThread::currentThread()) {
517-
qCCritical(qdssetup) << "Stopping datasync thread" << thread->name()
518-
<< "from the main thread even though it was created on a different thread!"
519-
<< "This can lead to synchronization errors. You should always stop all other threads before quitting the application.";
520-
}
520+
// stop/remove all running setups
521+
for(auto it = threads.constBegin(); it != threads.constEnd(); it++) {
522+
const auto &thread = it.value();
523+
if(thread) //active setup -> send stip
524+
thread->stopEngine();
525+
else //passive setup -> simply remove defaults
526+
DefaultsPrivate::removeDefaults(it.key());
521527
}
522-
for(auto &thread : threads)
523-
thread->waitAndTerminate(timeout);
524528

525-
DefaultsPrivate::clearDefaults();
529+
// wait for active setups to finish
530+
for(auto &thread : threads) {
531+
if(thread)
532+
thread->waitAndTerminate(timeout);
533+
}
526534
}
527535

528536
unsigned long SetupPrivate::currentTimeout()
@@ -595,9 +603,14 @@ SetupPrivate::SetupPrivate() :
595603
{Defaults::SignScheme, Setup::ECDSA_ECP_SHA3_512},
596604
{Defaults::CryptScheme, Setup::ECIES_ECP_SHA3_512},
597605
{Defaults::SymScheme, Setup::AES_EAX}
598-
}
606+
}
599607
{}
600608

609+
void SetupPrivate::deleteThread(EngineThread *thread)
610+
{
611+
thread->deleteLater();
612+
}
613+
601614
// ------------- Exceptions -------------
602615

603616
SetupException::SetupException(const QString &setupName, const QString &message) :

src/datasync/setup_p.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include <QtCore/QMutex>
55
#include <QtCore/QThread>
6-
#include <QtCore/QPointer>
6+
#include <QtCore/QSharedPointer>
77

88
#include <QtJsonSerializer/QJsonSerializer>
99

@@ -18,7 +18,8 @@ namespace QtDataSync {
1818
//must be exported for tests
1919
class Q_DATASYNC_EXPORT SetupPrivate
2020
{
21-
friend class Setup;
21+
friend class QtDataSync::Setup;
22+
friend class QtDataSync::EngineThread;
2223

2324
public:
2425
static void cleanupHandler();
@@ -35,7 +36,7 @@ class Q_DATASYNC_EXPORT SetupPrivate
3536
static const QString DefaultLocalDir;
3637

3738
static QMutex setupMutex;
38-
static QHash<QString, QPointer<EngineThread>> engines;
39+
static QHash<QString, QSharedPointer<EngineThread>> engines;
3940
static unsigned long timeout;
4041

4142
QString localDir;
@@ -47,6 +48,8 @@ class Q_DATASYNC_EXPORT SetupPrivate
4748
ExchangeEngine::ImportData initialImport;
4849

4950
SetupPrivate();
51+
52+
static void deleteThread(EngineThread *thread);
5053
};
5154

5255
}

0 commit comments

Comments
 (0)