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:
Klaus Basan
2014-07-01 19:11:25 +02:00
parent c03d45123d
commit d9a1c0cc8c
16 changed files with 522 additions and 229 deletions

View 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