mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-31 12:55:33 +08:00
Ref T314, thread CContinuousWorker::finish
- use application thread if there is no owner - use QPointer for owner - more detailled error message
This commit is contained in:
@@ -118,6 +118,7 @@ namespace BlackCore
|
||||
QCoreApplication::setApplicationName(m_applicationName);
|
||||
QCoreApplication::setApplicationVersion(CBuildConfig::getVersionString());
|
||||
this->setObjectName(m_applicationName);
|
||||
this->thread()->setObjectName(m_applicationName); // normally no effect as thread already runs, but does not harm either
|
||||
|
||||
// init skipped when called from CGuiApplication
|
||||
if (init)
|
||||
|
||||
@@ -31,6 +31,12 @@ namespace BlackMisc
|
||||
return (QCoreApplication::instance()->thread() == toBeTested->thread());
|
||||
}
|
||||
|
||||
bool CThreadUtils::isApplicationThread(const QThread *toBeTested)
|
||||
{
|
||||
if (!toBeTested || !QCoreApplication::instance() || !QCoreApplication::instance()->thread()) { return false; }
|
||||
return (QCoreApplication::instance()->thread() == toBeTested);
|
||||
}
|
||||
|
||||
bool CThreadUtils::isCurrentThreadApplicationThread()
|
||||
{
|
||||
if (!QCoreApplication::instance()) { return false; }
|
||||
@@ -68,11 +74,15 @@ namespace BlackMisc
|
||||
return s.arg(reinterpret_cast<long long>(t), 0, 16);
|
||||
}
|
||||
|
||||
const QString CThreadUtils::currentThreadInfo()
|
||||
const QString CThreadUtils::threadInfo(QThread *thread)
|
||||
{
|
||||
static const QString info("thread: %1 name: '%2' priority: '%3'");
|
||||
const QThread *t = QThread::currentThread();
|
||||
if (!t) { return QString("no thread"); }
|
||||
return info.arg(threadToString(t), t->objectName(), priorityToString(t->priority()));
|
||||
if (!thread) { return QString("no thread"); }
|
||||
return info.arg(threadToString(thread), thread->objectName(), priorityToString(thread->priority()));
|
||||
}
|
||||
|
||||
const QString CThreadUtils::currentThreadInfo()
|
||||
{
|
||||
return threadInfo(QThread::currentThread());
|
||||
}
|
||||
} // ns
|
||||
|
||||
@@ -34,6 +34,10 @@ namespace BlackMisc
|
||||
//! \remarks can be used as ASSERT check for threaded objects
|
||||
static bool isApplicationThreadObjectThread(const QObject *toBeTested);
|
||||
|
||||
//! Is the application thread the QObject's thread?
|
||||
//! \remarks can be used as ASSERT check for threaded objects
|
||||
static bool isApplicationThread(const QThread *toBeTested);
|
||||
|
||||
//! Is the current thread the Application thread?
|
||||
//! \remarks can be used as ASSERT check for threaded objects
|
||||
static bool isCurrentThreadApplicationThread();
|
||||
@@ -44,6 +48,9 @@ namespace BlackMisc
|
||||
//! Thread to int string info
|
||||
static const QString threadToString(const void *t);
|
||||
|
||||
//! Info about current thread
|
||||
static const QString threadInfo(QThread *thread);
|
||||
|
||||
//! Info about current thread
|
||||
static const QString currentThreadInfo();
|
||||
};
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
#include <future>
|
||||
#include <QTimer>
|
||||
#include <QPointer>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace BlackMisc
|
||||
@@ -149,7 +150,7 @@ namespace BlackMisc
|
||||
if (m_owner)
|
||||
{
|
||||
const QString ownerName = m_owner->objectName().isEmpty() ? m_owner->metaObject()->className() : m_owner->objectName();
|
||||
thread->setObjectName(ownerName + ":" + m_name);
|
||||
thread->setObjectName(ownerName + ": " + m_name);
|
||||
}
|
||||
|
||||
moveToThread(thread);
|
||||
@@ -175,12 +176,12 @@ namespace BlackMisc
|
||||
{
|
||||
this->quit();
|
||||
|
||||
// already in different thread? otherwise return
|
||||
// already in application (main) thread? => return
|
||||
if (CThreadUtils::isApplicationThreadObjectThread(this)) { return; }
|
||||
|
||||
// Called by own thread, will deadlock, return
|
||||
// called by own thread, will deadlock, return
|
||||
if (CThreadUtils::isCurrentThreadObjectThread(this)) { return; }
|
||||
auto *ownThread = thread();
|
||||
QThread *ownThread = thread();
|
||||
|
||||
const qint64 beforeWait = QDateTime::currentMSecsSinceEpoch();
|
||||
ownThread->wait(30 * 1000); //! \todo KB 2017-10 temp workaround: in T145 this will be fixed, sometimes (very rarely) hanging here during shutdown
|
||||
@@ -225,24 +226,49 @@ namespace BlackMisc
|
||||
{
|
||||
this->setFinished();
|
||||
|
||||
QThread *ownThread = thread();
|
||||
if (!ownThread) { return; } // normally never happening
|
||||
moveToThread(ownThread->thread()); // move worker back to the thread which constructed it, so there is no race on deletion
|
||||
|
||||
// very sporadic crash here, changing to QSingleShot version for testing
|
||||
// QMetaObject::invokeMethod(ownThread, "deleteLater");
|
||||
// QMetaObject::invokeMethod(this, "deleteLater");
|
||||
QPointer<QThread> pOwnThread(ownThread);
|
||||
QPointer<QThread> pOwnThreadBeforeMove(this->thread()); // thread before moved back
|
||||
QPointer<CContinuousWorker> myself(this);
|
||||
QTimer::singleShot(0, ownThread, [ = ]
|
||||
|
||||
if (!m_owner || (pOwnThreadBeforeMove.data() != m_owner->thread()))
|
||||
{
|
||||
if (!pOwnThread) { return; }
|
||||
pOwnThread->deleteLater();
|
||||
});
|
||||
QTimer::singleShot(0, this, [ = ]
|
||||
if (m_owner)
|
||||
{
|
||||
// we have a owner, and the owner thread is not the same as the worker thread
|
||||
// move worker back to the thread which constructed it, so there is no race on deletion
|
||||
this->moveToThread(m_owner->thread());
|
||||
Q_ASSERT_X(this->thread() == m_owner->thread(), Q_FUNC_INFO, "Thread mismatch owner");
|
||||
}
|
||||
else if (qApp && qApp->thread())
|
||||
{
|
||||
// no owner, we move to main thread
|
||||
this->moveToThread(qApp->thread());
|
||||
Q_ASSERT_X(this->thread() == qApp->thread(), Q_FUNC_INFO, "Thread mismatch qApp");
|
||||
}
|
||||
|
||||
// if thread still exists and is not the main thread, delete it
|
||||
if (pOwnThreadBeforeMove && !CThreadUtils::isApplicationThread(pOwnThreadBeforeMove.data()))
|
||||
{
|
||||
// delete the original "background" thread
|
||||
QTimer::singleShot(0, pOwnThreadBeforeMove.data(), [ = ]
|
||||
{
|
||||
if (pOwnThreadBeforeMove) { pOwnThreadBeforeMove->deleteLater(); }
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!myself) { return; }
|
||||
myself->deleteLater();
|
||||
});
|
||||
// getting here would mean we have a owner and the owner thread is the same as the work thread
|
||||
const QString msg = QString("Owner thread: '%1' own thread: '%2'").arg(CThreadUtils::threadInfo(m_owner->thread()), CThreadUtils::threadInfo(pOwnThreadBeforeMove.data()));
|
||||
BLACK_VERIFY_X(false, Q_FUNC_INFO, msg.toLatin1().constData());
|
||||
}
|
||||
|
||||
if (myself)
|
||||
{
|
||||
// if worker still exists delete it
|
||||
QTimer::singleShot(0, myself.data(), [ = ]
|
||||
{
|
||||
if (myself) { myself->deleteLater(); }
|
||||
});
|
||||
}
|
||||
}
|
||||
} // ns
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace BlackMisc
|
||||
using CWorkerBase::setStarted;
|
||||
using CWorkerBase::setFinished;
|
||||
|
||||
QObject *m_owner = nullptr;
|
||||
QPointer<QObject> m_owner; //!< owner, QPointer will detect if the owner is deleted
|
||||
QString m_name; //!< worker's name
|
||||
std::atomic<bool> m_enabled { true }; //!< marker it is enabled
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user