mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-03 15:45:46 +08:00
refs #287 Thread safety, fix "memory access violation"
* some improved comments and information * QMetaObject::invokeMethod in tool.cpp as thread safe invocation * common base class for threaded readers * removed event class, using QMetaObject::invoke instead for forcing calls in main event loop * stop methods for readers, as used for graceful shutdown (preparing for thread safe destruction of objects) * graceful shutdown for network context * calls in tool now via inkoke for thread safety (only thread safe methods called directly)
This commit is contained in:
146
src/blackmisc/threadedreader.h
Normal file
146
src/blackmisc/threadedreader.h
Normal file
@@ -0,0 +1,146 @@
|
||||
#ifndef BLACKMISC_THREADED_READER_H
|
||||
#define BLACKMISC_THREADED_READER_H
|
||||
|
||||
//! \file
|
||||
|
||||
#include <QReadWriteLock>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include <QNetworkReply>
|
||||
#include <QFuture>
|
||||
#include <QCoreApplication>
|
||||
|
||||
// Header only class, to avoid orward instantiation across subprojects
|
||||
|
||||
namespace BlackMisc
|
||||
{
|
||||
/*!
|
||||
* Support for threaded based reading and parsing tasks such
|
||||
* as data files via http, or file system and parsing (such as FSX models)
|
||||
*/
|
||||
template <class FutureRet = void> class CThreadedReader
|
||||
{
|
||||
|
||||
public:
|
||||
//! Destructor
|
||||
virtual ~CThreadedReader()
|
||||
{
|
||||
delete m_updateTimer;
|
||||
this->stop();
|
||||
}
|
||||
|
||||
//! Thread safe, set update timestamp
|
||||
//! \threadsafe
|
||||
QDateTime getUpdateTimestamp() const
|
||||
{
|
||||
QReadLocker(&this->m_lock);
|
||||
return this->m_updateTimestamp;
|
||||
}
|
||||
|
||||
//! Thread safe, set update timestamp
|
||||
//! \threadsafe
|
||||
void setUpdateTimestamp(const QDateTime &updateTimestamp)
|
||||
{
|
||||
QWriteLocker(&this->m_lock);
|
||||
this->m_updateTimestamp = updateTimestamp;
|
||||
}
|
||||
|
||||
//! Thread safe, mark as stopped
|
||||
virtual void stop()
|
||||
{
|
||||
if (this->isStopped()) return;
|
||||
this->setStopFlag();
|
||||
this->setInterval(0);
|
||||
|
||||
// shutdown pending
|
||||
if (this->m_pendingFuture.isRunning())
|
||||
{
|
||||
// cancel does not work with all futures, especially not with QConcurrent::run
|
||||
// the stop flag should the job
|
||||
// but I will cancel anyway
|
||||
this->m_pendingFuture.cancel();
|
||||
}
|
||||
if (this->m_pendingNetworkReply && this->m_pendingNetworkReply->isRunning())
|
||||
{
|
||||
this->m_pendingNetworkReply->abort();
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 250); // allow the abort to be called
|
||||
}
|
||||
|
||||
// cancel or stop flag above should terminate QFuture
|
||||
this->m_pendingFuture.waitForFinished();
|
||||
|
||||
}
|
||||
|
||||
//! Thread safe, is in state stopped?
|
||||
//! \threadsafe
|
||||
bool isStopped() const
|
||||
{
|
||||
QReadLocker rl(&this->m_lock);
|
||||
return this->m_stopped;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the update time
|
||||
* \param updatePeriodMs <=0 stops the timer
|
||||
* \threadsafe
|
||||
*/
|
||||
void setInterval(int updatePeriodMs)
|
||||
{
|
||||
Q_ASSERT(this->m_updateTimer);
|
||||
QWriteLocker(&this->m_lock);
|
||||
if (updatePeriodMs < 1)
|
||||
this->m_updateTimer->stop();
|
||||
else
|
||||
this->m_updateTimer->start(updatePeriodMs);
|
||||
}
|
||||
|
||||
//! Get the timer interval (ms)
|
||||
//! \threadsafe
|
||||
int interval() const
|
||||
{
|
||||
QReadLocker rl(&this->m_lock);
|
||||
return this->m_updateTimer->interval();
|
||||
}
|
||||
|
||||
protected:
|
||||
//! Constructor
|
||||
CThreadedReader() :
|
||||
m_updateTimer(nullptr), m_stopped(false), m_pendingNetworkReply(nullptr), m_lock(QReadWriteLock::Recursive)
|
||||
{
|
||||
this->m_updateTimer = new QTimer();
|
||||
}
|
||||
|
||||
//! Has pending network replay
|
||||
//! \threadsafe
|
||||
void setPendingNetworkReply(QNetworkReply *reply)
|
||||
{
|
||||
QWriteLocker(&this->m_lock);
|
||||
this->m_pendingNetworkReply = reply;
|
||||
}
|
||||
|
||||
//! Has pending operation
|
||||
//! \threadsafe
|
||||
void setPendingFuture(QFuture<FutureRet> future)
|
||||
{
|
||||
QWriteLocker(&this->m_lock);
|
||||
this->m_pendingFuture = future;
|
||||
}
|
||||
|
||||
//! Thread safe, mark as to be stopped
|
||||
//! \threadsafe
|
||||
void setStopFlag()
|
||||
{
|
||||
QWriteLocker wl(&this->m_lock);
|
||||
this->m_stopped = true;
|
||||
}
|
||||
|
||||
QDateTime m_updateTimestamp;
|
||||
QTimer *m_updateTimer;
|
||||
bool m_stopped;
|
||||
QFuture<FutureRet> m_pendingFuture; //!< optional future to be stopped
|
||||
QNetworkReply *m_pendingNetworkReply; //!< optional future to be stopped
|
||||
mutable QReadWriteLock m_lock;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
#endif // guard
|
||||
Reference in New Issue
Block a user