diff --git a/AbstractClient.cpp b/AbstractClient.cpp index 25195e1..275870d 100644 --- a/AbstractClient.cpp +++ b/AbstractClient.cpp @@ -16,7 +16,7 @@ QString AbstractClient::prettyName(bool dontTouchSocket) const QString type = socket && !dontTouchSocket ? (dynamic_cast(socket) ? "SSL" : "TCP") : "(NoSocket)"; QString port = socket && !dontTouchSocket && socket->peerPort() ? QString(":%1").arg(socket->peerPort()) : ""; QString ip = socket && !dontTouchSocket && !socket->peerAddress().isNull() ? socket->peerAddress().toString() : ""; - return QString("%1 %2 %3 %4%5").arg(id).arg(type).arg(!objectName().isNull()?objectName():"(AbstractSocket)").arg(ip).arg(port); + return QString("%1 %2 (id: %3) %4%5").arg(type).arg(!objectName().isNull()?objectName():"(AbstractSocket)").arg(id).arg(ip).arg(port); } diff --git a/EXClient.cpp b/EXClient.cpp index 459161d..afdb790 100644 --- a/EXClient.cpp +++ b/EXClient.cpp @@ -26,7 +26,7 @@ EXClient::EXClient(EXMgr *mgr, qint64 id, const QString &host, quint16 tport, qu EXClient::~EXClient() { Debug() << __FUNCTION__ << " host:" << host; - stop(); + stop(); ///< need to be sure to call this here rather than rely on ThreadObjectMixin, as by the time it runs, we lost our vtable } /// this should only be called from our thread, because it accesses socket which should only be touched from thread @@ -46,13 +46,8 @@ bool EXClient::isGood() const void EXClient::start() { - if (_thread.isRunning()) return; - Debug() << host << " starting thread"; - moveToThread(&_thread); - connect(&_thread, &QThread::started, this, &EXClient::on_started); - connect(&_thread, &QThread::finished, this, &EXClient::on_finished); + ThreadObjectMixin::start(); connect(this, &EXClient::sendRequest, this, &EXClient::_sendRequest); - _thread.start(); } void EXClient::stop() @@ -61,13 +56,7 @@ void EXClient::stop() /// ensure no new signals get sent to us after we switch back to the /// main thread. disconnect(this, &EXClient::sendRequest, this, &EXClient::_sendRequest); - if (_thread.isRunning()) { - Debug() << host << " thread is running, joining thread"; - _thread.quit(); - _thread.wait(); - } - disconnect(&_thread, &QThread::started, this, &EXClient::on_started); - disconnect(&_thread, &QThread::finished, this, &EXClient::on_finished); + ThreadObjectMixin::stop(); } // runs in thread @@ -81,7 +70,7 @@ void EXClient::on_started() void EXClient::on_finished() { killSocket(); - moveToThread(qApp->thread()); + ThreadObjectMixin::on_finished(); // calls moveToThread Debug() << "finished."; } diff --git a/EXClient.h b/EXClient.h index 880724f..59a665e 100644 --- a/EXClient.h +++ b/EXClient.h @@ -10,6 +10,7 @@ #include #include "Common.h" #include "AbstractClient.h" +#include "ThreadObjectMixin.h" struct EXResponse { @@ -32,7 +33,7 @@ Q_DECLARE_METATYPE(EXResponse); class EXMgr; -class EXClient : public AbstractClient +class EXClient : public AbstractClient, protected ThreadObjectMixin { Q_OBJECT public: @@ -72,11 +73,8 @@ protected: std::atomic lastConnectionAttempt = 0LL; ///< the last time we tried to reconnect - QThread _thread; - - void start(); ///< call from main thread - void stop(); ///< call from main thread - void restart() { stop(); start(); } ///< call from main thread + void start() override; ///< call from main thread + void stop() override; ///< call from main thread QString host; quint16 tport = 0, sport = 0; @@ -92,8 +90,9 @@ private: /// returns utf-8 encoded JSON data for a request static QByteArray makeRequestData(qint64 id, const QString &method, const QVariantList & params = QVariantList()); - void on_started(); - void on_finished(); + void on_started() override; + void on_finished() override; + QObject *qobj() override { return this; } void killSocket(); void reconnect(); }; diff --git a/EXMgr.cpp b/EXMgr.cpp index fbf1588..75d9abb 100644 --- a/EXMgr.cpp +++ b/EXMgr.cpp @@ -122,14 +122,15 @@ void EXMgr::checkClients() ///< called from the checkClientsTimer every 1 mins static const qint64 bad_timeout = 15*60*1000, // 15 mins low_server_timeout = checkClientsTimer->interval()/2; // 30 seconds Debug() << "EXMgr: Checking clients..."; - int lagCt = 0; const bool lowServers = height.seenBy.count() == 0; const qint64 stale_timeout = lowServers ? low_server_timeout : EXClient::reconnectTime; // 1 or 2 mins + QStringList laggers; for (EXClient *client : clients) { const auto now = Util::getTime(); - const bool lagging = client->info.height < height.height && client->info.isValid(); - if (client->isGood() && !client->isStale()) { - if (lagging) ++lagCt; + if (const bool lagging = client->info.height < height.height && client->info.isValid(); + client->isGood() && !client->isStale()) + { + if (lagging) laggers += QString("%1 (%2)").arg(client->host).arg(client->info.height); continue; } qint64 elapsed = qMin(now-client->lastConnectionAttempt, now-client->lastGood); @@ -144,8 +145,9 @@ void EXMgr::checkClients() ///< called from the checkClientsTimer every 1 mins client->restart(); } } - if (lagCt) { - Log() << lagCt << " servers are lagging behind the latest block height"; + if (int ct = laggers.count(); ct) { + QString s = ct == 1 ? " is" : "s are"; + Log("%d server%s lagging behind the latest block height of %d: %s", ct, s.toUtf8().constData(), height.height, laggers.join(", ").toUtf8().constData()); } } diff --git a/ShuffleUpSrv.pro b/ShuffleUpSrv.pro index 6e69856..df36bc6 100644 --- a/ShuffleUpSrv.pro +++ b/ShuffleUpSrv.pro @@ -26,7 +26,7 @@ SOURCES += \ Options.cpp \ SrvMgr.cpp \ TcpServer.cpp \ - ThreadObject.cpp \ + ThreadObjectMixin.cpp \ bitcoin/base58.cpp \ bitcoin/crypto/aes.cpp \ bitcoin/crypto/chacha20.cpp \ @@ -64,7 +64,7 @@ HEADERS += \ Options.h \ SrvMgr.h \ TcpServer.h \ - ThreadObject.h \ + ThreadObjectMixin.h \ Util.h \ EXMgr.h \ Common.h \ diff --git a/ThreadObject.cpp b/ThreadObject.cpp deleted file mode 100644 index 0f30a6a..0000000 --- a/ThreadObject.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "ThreadObject.h" - -ThreadObjectMixin::ThreadObjectMixin() -{ - Q_ASSERT(qobj()); -} - -ThreadObjectMixin::~ThreadObjectMixin() -{} - -QString ThreadObjectMixin::prettyName() const -{ - return QString(qobj()->objectName().isNull() ? "(ThreadObject)" : qobj()->objectName()); -} - - -void ThreadObjectMixin::start() -{ - if (_thread.isRunning()) - return; - chan.clear(); - qobj()->moveToThread(&_thread); - conns.push_back(qobj()->connect(&_thread, &QThread::started, qobj(), [this](){on_started();})); - conns.push_back(qobj()->connect(&_thread, &QThread::finished, qobj(), [this](){on_finished();})); - _thread.start(); -} - -void ThreadObjectMixin::stop() -{ - if (_thread.isRunning()) { - Debug() << prettyName() << " thread is running, joining thread"; - _thread.quit(); - _thread.wait(); - } - for (auto c : conns) { - qobj()->disconnect(c); - } - conns.clear(); -} - -void ThreadObjectMixin::on_started() -{ -} - -void ThreadObjectMixin::on_finished() -{ - qobj()->moveToThread(qApp->thread()); -} diff --git a/ThreadObjectMixin.cpp b/ThreadObjectMixin.cpp new file mode 100644 index 0000000..b8f2ba7 --- /dev/null +++ b/ThreadObjectMixin.cpp @@ -0,0 +1,49 @@ +#include "ThreadObjectMixin.h" + +ThreadObjectMixin::ThreadObjectMixin() +{ + origThread = QThread::currentThread(); + origThread->connect(origThread, &QThread::finished, [this]{ + Warning() << "ThreadObjectMixin " << qobj()->objectName() << ": original thread ended! Settings original thread to main thread! FIXME!"; + origThread = qApp->thread(); + }); +} + +ThreadObjectMixin::~ThreadObjectMixin() +{ + stop(); // this is here for paranoia reasons. In normal app function derived class should make sure it's stopped before we get here. This should be a NO-OP! +} + + +void ThreadObjectMixin::start() +{ + if (_thread.isRunning()) return; + Debug() << qobj()->objectName() << " starting thread"; + chan.clear(); + qobj()->moveToThread(&_thread); + conns.push_back(qobj()->connect(&_thread, &QThread::started, qobj(), [this](){on_started();})); + conns.push_back(qobj()->connect(&_thread, &QThread::finished, qobj(), [this](){on_finished();})); + _thread.start(); +} + +void ThreadObjectMixin::stop() +{ + if (_thread.isRunning()) { + Debug() << qobj()->objectName() << " thread is running, joining thread"; + _thread.quit(); + _thread.wait(); + } + for (auto c : conns) { + qobj()->disconnect(c); + } + conns.clear(); +} + +void ThreadObjectMixin::on_started() +{ +} + +void ThreadObjectMixin::on_finished() +{ + qobj()->moveToThread(origThread); +} diff --git a/ThreadObject.h b/ThreadObjectMixin.h similarity index 59% rename from ThreadObject.h rename to ThreadObjectMixin.h index 1615bf4..3b293b3 100644 --- a/ThreadObject.h +++ b/ThreadObjectMixin.h @@ -1,5 +1,5 @@ -#ifndef THREADOBJECT_H -#define THREADOBJECT_H +#ifndef THREADOBJECT_MIXIN_H +#define THREADOBJECT_MIXIN_H #include #include @@ -12,21 +12,19 @@ class ThreadObjectMixin public: ThreadObjectMixin(); virtual ~ThreadObjectMixin(); - virtual QString prettyName() const; - - QObject *qobj() const { return dynamic_cast(const_cast(this)); } protected: - QThread _thread; + QThread _thread, *origThread = nullptr; Util::Channel chan; QList conns; + virtual QObject *qobj() = 0; ///< reimplement in subclasses to return the QObject pointer (this) virtual void start(); ///< derived classes should call super implementation virtual void stop(); ///< derived classes should call super implementation virtual void restart() { stop(); start(); } protected slots: - virtual void on_started(); ///< default impll does nothing - virtual void on_finished(); ///< be sure to chain to this if you override and call it. + virtual void on_started(); ///< default impl does nothing + virtual void on_finished(); ///< be sure to call this if you override. (does moveToThread(mainthread)) }; -#endif // THREADOBJECT_H +#endif // THREADOBJECT_MIXIN_H