Browse Source

QGCCacheWorker fixes

* Fix racey access of `QGCCacheWorker::_taskQueue`
* `QGCCacheWorker::_valid` needs to be atomic
* Use `QScopedPointer<QSqlDatabase>` for safety
* Some refactoring for readability & debugging
QGC4.4
Keith Bennett 4 years ago committed by Don Gagne
parent
commit
ee44c137f0
  1. 191
      src/QtLocationPlugin/QGCTileCacheWorker.cpp
  2. 37
      src/QtLocationPlugin/QGCTileCacheWorker.h

191
src/QtLocationPlugin/QGCTileCacheWorker.cpp

@ -76,12 +76,12 @@ QGCCacheWorker::quit()
if(_hostLookupID) { if(_hostLookupID) {
QHostInfo::abortHostLookup(_hostLookupID); QHostInfo::abortHostLookup(_hostLookupID);
} }
_mutex.lock(); QMutexLocker lock(&_taskQueueMutex);
while(_taskQueue.count()) { while(_taskQueue.count()) {
QGCMapTask* task = _taskQueue.dequeue(); QGCMapTask* task = _taskQueue.dequeue();
delete task; delete task;
} }
_mutex.unlock(); lock.unlock(); // don't need the lock any more
if(this->isRunning()) { if(this->isRunning()) {
_waitc.wakeAll(); _waitc.wakeAll();
} }
@ -97,9 +97,9 @@ QGCCacheWorker::enqueueTask(QGCMapTask* task)
task->deleteLater(); task->deleteLater();
return false; return false;
} }
_mutex.lock(); QMutexLocker lock(&_taskQueueMutex);
_taskQueue.enqueue(task); _taskQueue.enqueue(task);
_mutex.unlock(); lock.unlock(); // don't need to hold the mutex any more
if(this->isRunning()) { if(this->isRunning()) {
_waitc.wakeAll(); _waitc.wakeAll();
} else { } else {
@ -116,61 +116,19 @@ QGCCacheWorker::run()
_init(); _init();
} }
if(_valid) { if(_valid) {
_db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession)); _connectDB();
_db->setDatabaseName(_databasePath);
_db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
_valid = _db->open();
} }
_deleteBingNoTileTiles(); _deleteBingNoTileTiles();
QMutexLocker lock(&_taskQueueMutex);
while(true) { while(true) {
QGCMapTask* task; QGCMapTask* task;
if(_taskQueue.count()) { if(_taskQueue.count()) {
_mutex.lock();
task = _taskQueue.dequeue(); task = _taskQueue.dequeue();
_mutex.unlock();
switch(task->type()) { // Don't need the lock while running the task.
case QGCMapTask::taskInit: lock.unlock();
break; _runTask(task);
case QGCMapTask::taskCacheTile: lock.relock();
_saveTile(task);
break;
case QGCMapTask::taskFetchTile:
_getTile(task);
break;
case QGCMapTask::taskFetchTileSets:
_getTileSets(task);
break;
case QGCMapTask::taskCreateTileSet:
_createTileSet(task);
break;
case QGCMapTask::taskGetTileDownloadList:
_getTileDownloadList(task);
break;
case QGCMapTask::taskUpdateTileDownloadState:
_updateTileDownloadState(task);
break;
case QGCMapTask::taskDeleteTileSet:
_deleteTileSet(task);
break;
case QGCMapTask::taskRenameTileSet:
_renameTileSet(task);
break;
case QGCMapTask::taskPruneCache:
_pruneCache(task);
break;
case QGCMapTask::taskReset:
_resetCacheDatabase(task);
break;
case QGCMapTask::taskExport:
_exportSets(task);
break;
case QGCMapTask::taskImport:
_importSets(task);
break;
case QGCMapTask::taskTestInternet:
_testInternet();
break;
}
task->deleteLater(); task->deleteLater();
//-- Check for update timeout //-- Check for update timeout
size_t count = static_cast<size_t>(_taskQueue.count()); size_t count = static_cast<size_t>(_taskQueue.count());
@ -181,29 +139,75 @@ QGCCacheWorker::run()
} }
if(!count || (time(nullptr) - _lastUpdate > _updateTimeout)) { if(!count || (time(nullptr) - _lastUpdate > _updateTimeout)) {
if(_valid) { if(_valid) {
// _updateTotals() will emit a signal. Don't keep the lock
// while any slots process the signal.
lock.unlock();
_updateTotals(); _updateTotals();
lock.relock();
} }
} }
} else { } else {
//-- Wait a bit before shutting things down //-- Wait a bit before shutting things down
_waitmutex.lock(); unsigned long timeoutMilliseconds = 5000;
unsigned long timeout = 5000; _waitc.wait(lock.mutex(), timeoutMilliseconds);
_waitc.wait(&_waitmutex, timeout);
_waitmutex.unlock();
_mutex.lock();
//-- If nothing to do, close db and leave thread //-- If nothing to do, close db and leave thread
if(!_taskQueue.count()) { if(!_taskQueue.count()) {
_mutex.unlock();
break; break;
} }
_mutex.unlock();
} }
} }
if(_db) { lock.unlock();
delete _db; _disconnectDB();
_db = nullptr; }
QSqlDatabase::removeDatabase(kSession);
//-----------------------------------------------------------------------------
void
QGCCacheWorker::_runTask(QGCMapTask *task)
{
switch(task->type()) {
case QGCMapTask::taskInit:
return;
case QGCMapTask::taskCacheTile:
_saveTile(task);
return;
case QGCMapTask::taskFetchTile:
_getTile(task);
return;
case QGCMapTask::taskFetchTileSets:
_getTileSets(task);
return;
case QGCMapTask::taskCreateTileSet:
_createTileSet(task);
return;
case QGCMapTask::taskGetTileDownloadList:
_getTileDownloadList(task);
return;
case QGCMapTask::taskUpdateTileDownloadState:
_updateTileDownloadState(task);
return;
case QGCMapTask::taskDeleteTileSet:
_deleteTileSet(task);
return;
case QGCMapTask::taskRenameTileSet:
_renameTileSet(task);
return;
case QGCMapTask::taskPruneCache:
_pruneCache(task);
return;
case QGCMapTask::taskReset:
_resetCacheDatabase(task);
return;
case QGCMapTask::taskExport:
_exportSets(task);
return;
case QGCMapTask::taskImport:
_importSets(task);
return;
case QGCMapTask::taskTestInternet:
_testInternet();
return;
} }
qCWarning(QGCTileCacheLog) << "_runTask given unhandled task type" << task->type();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -703,7 +707,7 @@ QGCCacheWorker::_resetCacheDatabase(QGCMapTask* mtask)
query.exec(s); query.exec(s);
s = QString("DROP TABLE TilesDownload"); s = QString("DROP TABLE TilesDownload");
query.exec(s); query.exec(s);
_valid = _createDB(_db); _valid = _createDB(*_db);
task->setResetCompleted(); task->setResetCompleted();
} }
@ -718,11 +722,7 @@ QGCCacheWorker::_importSets(QGCMapTask* mtask)
//-- If replacing, simply copy over it //-- If replacing, simply copy over it
if(task->replace()) { if(task->replace()) {
//-- Close and delete old database //-- Close and delete old database
if(_db) { _disconnectDB();
delete _db;
_db = nullptr;
QSqlDatabase::removeDatabase(kSession);
}
QFile file(_databasePath); QFile file(_databasePath);
file.remove(); file.remove();
//-- Copy given database //-- Copy given database
@ -731,10 +731,7 @@ QGCCacheWorker::_importSets(QGCMapTask* mtask)
_init(); _init();
if(_valid) { if(_valid) {
task->setProgress(50); task->setProgress(50);
_db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession)); _connectDB();
_db->setDatabaseName(_databasePath);
_db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
_valid = _db->open();
} }
task->setProgress(100); task->setProgress(100);
} else { } else {
@ -905,11 +902,11 @@ QGCCacheWorker::_exportSets(QGCMapTask* mtask)
QFile file(task->path()); QFile file(task->path());
file.remove(); file.remove();
//-- Create exported database //-- Create exported database
QSqlDatabase *dbExport = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kExportSession)); QScopedPointer<QSqlDatabase> dbExport(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kExportSession)));
dbExport->setDatabaseName(task->path()); dbExport->setDatabaseName(task->path());
dbExport->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE"); dbExport->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
if (dbExport->open()) { if (dbExport->open()) {
if(_createDB(dbExport, false)) { if(_createDB(*dbExport, false)) {
//-- Prepare progress report //-- Prepare progress report
quint64 tileCount = 0; quint64 tileCount = 0;
quint64 currentCount = 0; quint64 currentCount = 0;
@ -997,7 +994,7 @@ QGCCacheWorker::_exportSets(QGCMapTask* mtask)
qCritical() << "Map Cache SQL error (create export database):" << dbExport->lastError(); qCritical() << "Map Cache SQL error (create export database):" << dbExport->lastError();
task->setError("Error opening export database"); task->setError("Error opening export database");
} }
delete dbExport; dbExport.reset();
QSqlDatabase::removeDatabase(kExportSession); QSqlDatabase::removeDatabase(kExportSession);
task->setExportCompleted(); task->setExportCompleted();
} }
@ -1020,11 +1017,8 @@ QGCCacheWorker::_init()
if(!_databasePath.isEmpty()) { if(!_databasePath.isEmpty()) {
qCDebug(QGCTileCacheLog) << "Mapping cache directory:" << _databasePath; qCDebug(QGCTileCacheLog) << "Mapping cache directory:" << _databasePath;
//-- Initialize Database //-- Initialize Database
_db = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession)); if (_connectDB()) {
_db->setDatabaseName(_databasePath); _valid = _createDB(*_db);
_db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
if (_db->open()) {
_valid = _createDB(_db);
if(!_valid) { if(!_valid) {
_failed = true; _failed = true;
} }
@ -1032,9 +1026,7 @@ QGCCacheWorker::_init()
qCritical() << "Map Cache SQL error (init() open db):" << _db->lastError(); qCritical() << "Map Cache SQL error (init() open db):" << _db->lastError();
_failed = true; _failed = true;
} }
delete _db; _disconnectDB();
_db = nullptr;
QSqlDatabase::removeDatabase(kSession);
} else { } else {
qCritical() << "Could not find suitable cache directory."; qCritical() << "Could not find suitable cache directory.";
_failed = true; _failed = true;
@ -1045,10 +1037,21 @@ QGCCacheWorker::_init()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool bool
QGCCacheWorker::_createDB(QSqlDatabase* db, bool createDefault) QGCCacheWorker::_connectDB()
{
_db.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", kSession)));
_db->setDatabaseName(_databasePath);
_db->setConnectOptions("QSQLITE_ENABLE_SHARED_CACHE");
_valid = _db->open();
return _valid;
}
//-----------------------------------------------------------------------------
bool
QGCCacheWorker::_createDB(QSqlDatabase& db, bool createDefault)
{ {
bool res = false; bool res = false;
QSqlQuery query(*db); QSqlQuery query(db);
if(!query.exec( if(!query.exec(
"CREATE TABLE IF NOT EXISTS Tiles (" "CREATE TABLE IF NOT EXISTS Tiles ("
"tileID INTEGER PRIMARY KEY NOT NULL, " "tileID INTEGER PRIMARY KEY NOT NULL, "
@ -1116,12 +1119,12 @@ QGCCacheWorker::_createDB(QSqlDatabase* db, bool createDefault)
query.addBindValue(1); query.addBindValue(1);
query.addBindValue(QDateTime::currentDateTime().toTime_t()); query.addBindValue(QDateTime::currentDateTime().toTime_t());
if(!query.exec()) { if(!query.exec()) {
qWarning() << "Map Cache SQL error (Creating default tile set):" << db->lastError(); qWarning() << "Map Cache SQL error (Creating default tile set):" << db.lastError();
res = false; res = false;
} }
} }
} else { } else {
qWarning() << "Map Cache SQL error (Looking for default tile set):" << db->lastError(); qWarning() << "Map Cache SQL error (Looking for default tile set):" << db.lastError();
} }
} }
if(!res) { if(!res) {
@ -1133,6 +1136,16 @@ QGCCacheWorker::_createDB(QSqlDatabase* db, bool createDefault)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void void
QGCCacheWorker::_disconnectDB()
{
if (_db) {
_db.reset();
QSqlDatabase::removeDatabase(kSession);
}
}
//-----------------------------------------------------------------------------
void
QGCCacheWorker::_testInternet() QGCCacheWorker::_testInternet()
{ {
/* /*

37
src/QtLocationPlugin/QGCTileCacheWorker.h

@ -54,6 +54,8 @@ private slots:
void _lookupReady (QHostInfo info); void _lookupReady (QHostInfo info);
private: private:
void _runTask (QGCMapTask* task);
void _saveTile (QGCMapTask* mtask); void _saveTile (QGCMapTask* mtask);
void _getTile (QGCMapTask* mtask); void _getTile (QGCMapTask* mtask);
void _getTileSets (QGCMapTask* mtask); void _getTileSets (QGCMapTask* mtask);
@ -74,7 +76,9 @@ private:
bool _findTileSetID (const QString name, quint64& setID); bool _findTileSetID (const QString name, quint64& setID);
void _updateSetTotals (QGCCachedTileSet* set); void _updateSetTotals (QGCCachedTileSet* set);
bool _init (); bool _init ();
bool _createDB (QSqlDatabase *db, bool createDefault = true); bool _connectDB ();
bool _createDB (QSqlDatabase& db, bool createDefault = true);
void _disconnectDB ();
quint64 _getDefaultTileSet (); quint64 _getDefaultTileSet ();
void _updateTotals (); void _updateTotals ();
void _deleteTileSet (qulonglong id); void _deleteTileSet (qulonglong id);
@ -84,22 +88,21 @@ signals:
void internetStatus (bool active); void internetStatus (bool active);
private: private:
QQueue<QGCMapTask*> _taskQueue; QQueue<QGCMapTask*> _taskQueue;
QMutex _mutex; QMutex _taskQueueMutex;
QMutex _waitmutex; QWaitCondition _waitc;
QWaitCondition _waitc; QString _databasePath;
QString _databasePath; QScopedPointer<QSqlDatabase> _db;
QSqlDatabase* _db; std::atomic_bool _valid;
bool _valid; bool _failed;
bool _failed; quint64 _defaultSet;
quint64 _defaultSet; quint64 _totalSize;
quint64 _totalSize; quint32 _totalCount;
quint32 _totalCount; quint64 _defaultSize;
quint64 _defaultSize; quint32 _defaultCount;
quint32 _defaultCount; time_t _lastUpdate;
time_t _lastUpdate; int _updateTimeout;
int _updateTimeout; int _hostLookupID;
int _hostLookupID;
}; };
#endif // QGC_TILE_CACHE_WORKER_H #endif // QGC_TILE_CACHE_WORKER_H

Loading…
Cancel
Save