qa: Replace deprecated qVariantFromValue() by QVariant::fromValue()
[quassel.git] / src / core / sessionthread.cpp
index c2f9b5a..d8e8c4d 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2015 by the Quassel Project                        *
+ *   Copyright (C) 2005-2019 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
+#include "sessionthread.h"
+
+#include <QPointer>
+#include <QTimer>
+
 #include "core.h"
 #include "coresession.h"
 #include "internalpeer.h"
 #include "remotepeer.h"
-#include "sessionthread.h"
 #include "signalproxy.h"
 
-SessionThread::SessionThread(UserId uid, bool restoreState, QObject *parent)
-    : QThread(parent),
-    _session(0),
-    _user(uid),
-    _sessionInitialized(false),
-    _restoreState(restoreState)
-{
-    connect(this, SIGNAL(initialized()), this, SLOT(setSessionInitialized()));
-}
-
+namespace {
 
-SessionThread::~SessionThread()
+class Worker : public QObject
 {
-    // shut down thread gracefully
-    quit();
-    wait();
-}
+    Q_OBJECT
+
+public:
+    Worker(UserId userId, bool restoreState, bool strictIdentEnabled)
+        : _userId{userId}
+        , _restoreState{restoreState}
+        , _strictIdentEnabled{strictIdentEnabled}
+    {}
+
+public slots:
+    void initialize()
+    {
+        _session = new CoreSession{_userId, _restoreState, _strictIdentEnabled, this};
+        connect(_session, &QObject::destroyed, QThread::currentThread(), &QThread::quit);
+        connect(_session, &CoreSession::sessionStateReceived, Core::instance(), &Core::sessionStateReceived);
+        emit initialized();
+    }
 
+    void shutdown()
+    {
+        if (_session) {
+            _session->shutdown();
+        }
+    }
 
-CoreSession *SessionThread::session()
-{
-    return _session;
-}
+    void addClient(Peer* peer)
+    {
+        if (!_session) {
+            qWarning() << "Session not initialized!";
+            return;
+        }
+
+        auto remotePeer = qobject_cast<RemotePeer*>(peer);
+        if (remotePeer) {
+            _session->addClient(remotePeer);
+            return;
+        }
+        auto internalPeer = qobject_cast<InternalPeer*>(peer);
+        if (internalPeer) {
+            _session->addClient(internalPeer);
+            return;
+        }
+
+        qWarning() << "SessionThread::addClient() received invalid peer!" << peer;
+    }
 
+signals:
+    void initialized();
 
-UserId SessionThread::user()
-{
-    return _user;
-}
+private:
+    UserId _userId;
+    bool _restoreState;
+    bool _strictIdentEnabled;  ///< Whether or not strict ident mode is enabled, locking users' idents to Quassel username
+    QPointer<CoreSession> _session;
+};
 
+}  // namespace
 
-bool SessionThread::isSessionInitialized()
+SessionThread::SessionThread(UserId uid, bool restoreState, bool strictIdentEnabled, QObject* parent)
+    : QObject(parent)
 {
-    return _sessionInitialized;
+    auto worker = new Worker(uid, restoreState, strictIdentEnabled);
+    worker->moveToThread(&_sessionThread);
+    connect(&_sessionThread, &QThread::started, worker, &Worker::initialize);
+    connect(&_sessionThread, &QThread::finished, worker, &QObject::deleteLater);
+    connect(worker, &Worker::initialized, this, &SessionThread::onSessionInitialized);
+    connect(worker, &QObject::destroyed, this, &SessionThread::onSessionDestroyed);
+
+    connect(this, &SessionThread::addClientToWorker, worker, &Worker::addClient);
+    connect(this, &SessionThread::shutdownSession, worker, &Worker::shutdown);
+
+    // Defer thread start through the event loop, so the SessionThread instance is fully constructed before
+    QTimer::singleShot(0, &_sessionThread, SLOT(start()));
 }
 
-
-void SessionThread::setSessionInitialized()
+SessionThread::~SessionThread()
 {
-    _sessionInitialized = true;
-    foreach(QObject *peer, clientQueue) {
-        addClientToSession(peer);
-    }
-    clientQueue.clear();
+    // shut down thread gracefully
+    _sessionThread.quit();
+    _sessionThread.wait(30000);
 }
 
-
-// this and the following related methods are executed in the Core thread!
-void SessionThread::addClient(QObject *peer)
+void SessionThread::shutdown()
 {
-    if (isSessionInitialized()) {
-        addClientToSession(peer);
-    }
-    else {
-        clientQueue.append(peer);
-    }
+    emit shutdownSession();
 }
 
-
-void SessionThread::addClientToSession(QObject *peer)
+void SessionThread::onSessionInitialized()
 {
-    RemotePeer *remote = qobject_cast<RemotePeer *>(peer);
-    if (remote) {
-        addRemoteClientToSession(remote);
-        return;
-    }
-
-    InternalPeer *internal = qobject_cast<InternalPeer *>(peer);
-    if (internal) {
-        addInternalClientToSession(internal);
-        return;
+    _sessionInitialized = true;
+    for (auto&& peer : _clientQueue) {
+        peer->setParent(nullptr);
+        peer->moveToThread(&_sessionThread);
+        emit addClientToWorker(peer);
     }
-
-    qWarning() << "SessionThread::addClient() received invalid peer!" << peer;
+    _clientQueue.clear();
 }
 
-
-void SessionThread::addRemoteClientToSession(RemotePeer *remotePeer)
+void SessionThread::onSessionDestroyed()
 {
-    remotePeer->setParent(0);
-    remotePeer->moveToThread(session()->thread());
-    emit addRemoteClient(remotePeer);
+    emit shutdownComplete(this);
 }
 
-
-void SessionThread::addInternalClientToSession(InternalPeer *internalPeer)
+void SessionThread::addClient(Peer* peer)
 {
-    internalPeer->setParent(0);
-    internalPeer->moveToThread(session()->thread());
-    emit addInternalClient(internalPeer);
+    if (_sessionInitialized) {
+        peer->setParent(nullptr);
+        peer->moveToThread(&_sessionThread);
+        emit addClientToWorker(peer);
+    }
+    else {
+        _clientQueue.push_back(peer);
+    }
 }
 
-
-void SessionThread::run()
-{
-    _session = new CoreSession(user(), _restoreState);
-    connect(this, SIGNAL(addRemoteClient(RemotePeer*)), _session, SLOT(addClient(RemotePeer*)));
-    connect(this, SIGNAL(addInternalClient(InternalPeer*)), _session, SLOT(addClient(InternalPeer*)));
-    connect(_session, SIGNAL(sessionState(Protocol::SessionState)), Core::instance(), SIGNAL(sessionState(Protocol::SessionState)));
-    emit initialized();
-    exec();
-    delete _session;
-}
+#include "sessionthread.moc"