Working on safer msg downloading/saving.

This commit is contained in:
Martin Rotter 2016-06-29 09:11:16 +02:00
parent a259cba741
commit d9311ae5d2
10 changed files with 44 additions and 64 deletions

View file

@ -27,12 +27,14 @@
FeedDownloader::FeedDownloader(QObject *parent) FeedDownloader::FeedDownloader(QObject *parent)
: QObject(parent), m_results(FeedDownloadResults()), m_feedsUpdated(0), m_feedsToUpdate(0), : QObject(parent), m_results(FeedDownloadResults()), m_msgUpdateMutex(new QMutex()), m_feedsUpdated(0), m_feedsToUpdate(0),
m_feedsUpdating(0), m_feedsTotalCount(0), m_stopUpdate(false) { m_feedsUpdating(0), m_feedsTotalCount(0), m_stopUpdate(false) {
qRegisterMetaType<FeedDownloadResults>("FeedDownloadResults"); qRegisterMetaType<FeedDownloadResults>("FeedDownloadResults");
} }
FeedDownloader::~FeedDownloader() { FeedDownloader::~FeedDownloader() {
m_msgUpdateMutex->unlock();
delete m_msgUpdateMutex;
qDebug("Destroying FeedDownloader instance."); qDebug("Destroying FeedDownloader instance.");
} }
@ -79,7 +81,7 @@ void FeedDownloader::updateFeeds(const QList<Feed*> &feeds) {
break; break;
} }
connect(feeds.at(i), SIGNAL(updated(int)), this, SLOT(oneFeedUpdateFinished(int)), connect(feeds.at(i), &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished,
(Qt::ConnectionType) (Qt::UniqueConnection | Qt::AutoConnection)); (Qt::ConnectionType) (Qt::UniqueConnection | Qt::AutoConnection));
QThreadPool::globalInstance()->start(feeds.at(i)); QThreadPool::globalInstance()->start(feeds.at(i));
@ -92,14 +94,24 @@ void FeedDownloader::stopRunningUpdate() {
m_stopUpdate = true; m_stopUpdate = true;
} }
void FeedDownloader::oneFeedUpdateFinished(int updated_messages) { void FeedDownloader::oneFeedUpdateFinished(const QList<Message> &messages) {
const Feed *feed = qobject_cast<Feed*>(sender()); Feed *feed = qobject_cast<Feed*>(sender());
disconnect(feed, SIGNAL(updated(int)), this, SLOT(oneFeedUpdateFinished(int))); disconnect(feed, &Feed::messagesObtained, this, &FeedDownloader::oneFeedUpdateFinished);
m_feedsUpdated++; m_feedsUpdated++;
m_feedsUpdating--; m_feedsUpdating--;
// Now make sure, that messages are actually stored to SQL in a locked state.
qDebug().nospace() << "Saving messages of feed "
<< feed->customId() << " in thread: \'"
<< QThread::currentThreadId() << "\'.";
m_msgUpdateMutex->lock();
int updated_messages = messages.isEmpty() ? 0 : feed->updateMessages(messages);
m_msgUpdateMutex->unlock();
if (updated_messages > 0) { if (updated_messages > 0) {
m_results.appendUpdatedFeed(QPair<QString,int>(feed->title(), updated_messages)); m_results.appendUpdatedFeed(QPair<QString,int>(feed->title(), updated_messages));
} }

View file

@ -22,6 +22,8 @@
#include <QPair> #include <QPair>
#include "core/message.h"
class Feed; class Feed;
@ -70,7 +72,7 @@ class FeedDownloader : public QObject {
void stopRunningUpdate(); void stopRunningUpdate();
private slots: private slots:
void oneFeedUpdateFinished(int updated_messages); void oneFeedUpdateFinished(const QList<Message> &messages);
signals: signals:
// Emitted if feed updates started. // Emitted if feed updates started.
@ -90,6 +92,7 @@ class FeedDownloader : public QObject {
void finalizeUpdate(); void finalizeUpdate();
FeedDownloadResults m_results; FeedDownloadResults m_results;
QMutex *m_msgUpdateMutex;
int m_feedsUpdated; int m_feedsUpdated;
int m_feedsToUpdate; int m_feedsToUpdate;

View file

@ -47,9 +47,7 @@ int main(int argc, char *argv[]) {
QObject::tr("LANG_AUTHOR"); QObject::tr("LANG_AUTHOR");
// Ensure that ini format is used as application settings storage on Mac OS. // Ensure that ini format is used as application settings storage on Mac OS.
#ifdef Q_OS_MAC
QSettings::setDefaultFormat(QSettings::IniFormat); QSettings::setDefaultFormat(QSettings::IniFormat);
#endif
// Setup debug output system. // Setup debug output system.
qInstallMessageHandler(Debugging::debugHandler); qInstallMessageHandler(Debugging::debugHandler);
@ -65,6 +63,7 @@ int main(int argc, char *argv[]) {
} }
// Register needed metatypes. // Register needed metatypes.
qRegisterMetaType<QList<Message> >("QList<Message>");
qRegisterMetaType<QList<RootItem*> >("QList<RootItem*>"); qRegisterMetaType<QList<RootItem*> >("QList<RootItem*>");
// Add an extra path for non-system icon themes and set current icon theme // Add an extra path for non-system icon themes and set current icon theme
@ -114,7 +113,7 @@ int main(int argc, char *argv[]) {
qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates(); qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->loadAllExpandStates();
// Setup single-instance behavior. // Setup single-instance behavior.
QObject::connect(&application, SIGNAL(messageReceived(QString)), &application, SLOT(processExecutionMessage(QString))); QObject::connect(&application, &Application::messageReceived, &application, &Application::processExecutionMessage);
if (qApp->isFirstRun() || qApp->isFirstRun(APP_VERSION)) { if (qApp->isFirstRun() || qApp->isFirstRun(APP_VERSION)) {
qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.\n\nPlease, check NEW stuff included in this\n" qApp->showGuiMessage(QSL(APP_NAME), QObject::tr("Welcome to %1.\n\nPlease, check NEW stuff included in this\n"

View file

@ -42,7 +42,7 @@
Application::Application(const QString &id, int &argc, char **argv) Application::Application(const QString &id, int &argc, char **argv)
: QtSingleApplication(id, argc, argv), : QtSingleApplication(id, argc, argv),
m_updateFeedsLock(nullptr), m_updateMessagesLock(nullptr), m_feedServices(QList<ServiceEntryPoint*>()), m_userActions(QList<QAction*>()), m_mainForm(nullptr), m_updateFeedsLock(nullptr), m_feedServices(QList<ServiceEntryPoint*>()), m_userActions(QList<QAction*>()), m_mainForm(nullptr),
m_trayIcon(nullptr), m_settings(nullptr), m_system(nullptr), m_skins(nullptr), m_trayIcon(nullptr), m_settings(nullptr), m_system(nullptr), m_skins(nullptr),
m_localization(nullptr), m_icons(nullptr), m_database(nullptr), m_downloadManager(nullptr) { m_localization(nullptr), m_icons(nullptr), m_database(nullptr), m_downloadManager(nullptr) {
connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit())); connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit()));
@ -127,16 +127,6 @@ Mutex *Application::feedUpdateLock() {
return m_updateFeedsLock.data(); return m_updateFeedsLock.data();
} }
Mutex *Application::messageUpdateLock() {
if (m_updateMessagesLock.isNull()) {
// NOTE: Cannot use parent hierarchy because this method can be usually called
// from any thread.
m_updateMessagesLock.reset(new Mutex());
}
return m_updateMessagesLock.data();
}
void Application::backupDatabaseSettings(bool backup_database, bool backup_settings, void Application::backupDatabaseSettings(bool backup_database, bool backup_settings,
const QString &target_path, const QString &backup_name) { const QString &target_path, const QString &backup_name) {
if (!QFileInfo(target_path).isWritable()) { if (!QFileInfo(target_path).isWritable()) {

View file

@ -113,7 +113,6 @@ class Application : public QtSingleApplication {
// Access to application-wide close lock. // Access to application-wide close lock.
Mutex *feedUpdateLock(); Mutex *feedUpdateLock();
Mutex *messageUpdateLock();
inline FormMain *mainForm() { inline FormMain *mainForm() {
return m_mainForm; return m_mainForm;
@ -187,7 +186,6 @@ class Application : public QtSingleApplication {
// tries to lock the lock for writing), then no other // tries to lock the lock for writing), then no other
// action will be allowed to lock for reading. // action will be allowed to lock for reading.
QScopedPointer<Mutex> m_updateFeedsLock; QScopedPointer<Mutex> m_updateFeedsLock;
QScopedPointer<Mutex> m_updateMessagesLock;
QList<ServiceEntryPoint*> m_feedServices; QList<ServiceEntryPoint*> m_feedServices;
QList<QAction*> m_userActions; QList<QAction*> m_userActions;

View file

@ -101,7 +101,9 @@ void IconFactory::loadCurrentIconTheme() {
// Display list of installed themes. // Display list of installed themes.
qDebug("Installed icon themes are: %s.", qDebug("Installed icon themes are: %s.",
qPrintable(QStringList(installed_themes).replaceInStrings(QRegExp(QSL("^|$")), QSL("\'")).join(QSL(", ")))); qPrintable(QStringList(installed_themes)
.replaceInStrings(QRegExp(QSL("^|$")), QSL("\'"))
.replaceInStrings(QRegExp(QSL("^\\'$")), QSL("\'\'")).join(QSL(", "))));
if (installed_themes.contains(theme_name_from_settings)) { if (installed_themes.contains(theme_name_from_settings)) {
// Desired icon theme is installed and can be loaded. // Desired icon theme is installed and can be loaded.
@ -111,7 +113,7 @@ void IconFactory::loadCurrentIconTheme() {
else { else {
// Desired icon theme is not currently available. // Desired icon theme is not currently available.
// Install "default" icon theme instead. // Install "default" icon theme instead.
qDebug("Icon theme '%s' cannot be loaded because it is not installed. No icon theme (or default icon theme) is loaded now.", qWarning("Icon theme '%s' cannot be loaded because it is not installed. No icon theme (or default icon theme) is loaded now.",
qPrintable(theme_name_from_settings)); qPrintable(theme_name_from_settings));
QIcon::setThemeName(APP_NO_THEME); QIcon::setThemeName(APP_NO_THEME);
} }

View file

@ -44,11 +44,13 @@ void Localization::loadActiveLanguage() {
qDebug("Starting to load active localization. Desired localization is '%s'.", qPrintable(desired_localization)); qDebug("Starting to load active localization. Desired localization is '%s'.", qPrintable(desired_localization));
if (app_translator->load(QLocale(desired_localization), "rssguard", QSL("-"), APP_LANG_PATH)) { if (app_translator->load(QLocale(desired_localization), "rssguard", QSL("-"), APP_LANG_PATH)) {
const QString real_loaded_locale = app_translator->translate("QObject", "LANG_ABBREV");
Application::installTranslator(app_translator); Application::installTranslator(app_translator);
qDebug("Application localization '%s' loaded successfully, specifically sublocalization '%s' was loaded.", qDebug("Application localization '%s' loaded successfully, specifically sublocalization '%s' was loaded.",
qPrintable(desired_localization), qPrintable(desired_localization),
qPrintable(app_translator->translate("QObject", "LANG_ABBREV"))); qPrintable(real_loaded_locale));
desired_localization = app_translator->translate("QObject", "LANG_ABBREV"); desired_localization = real_loaded_locale;
} }
else { else {
qWarning("Application localization '%s' was not loaded. Loading '%s' instead.", qWarning("Application localization '%s' was not loaded. Loading '%s' instead.",

View file

@ -59,7 +59,7 @@ void SkinFactory::loadCurrentSkin() {
} }
} }
qFatal("Failed to load selected or default skin(s). Quitting!"); qFatal("Failed to load selected or default skin. Quitting!");
} }
void SkinFactory::loadSkinFromData(const Skin &skin) { void SkinFactory::loadSkinFromData(const Skin &skin) {

View file

@ -88,17 +88,6 @@ void Feed::setCountOfUnreadMessages(int count_unread_messages) {
m_unreadCount = count_unread_messages; m_unreadCount = count_unread_messages;
} }
int Feed::update() {
QList<Message> msgs = obtainNewMessages();
if (msgs.size() > 0) {
return updateMessages(msgs);
}
else {
return 0;
}
}
void Feed::setAutoUpdateInitialInterval(int auto_update_interval) { void Feed::setAutoUpdateInitialInterval(int auto_update_interval) {
// If new initial auto-update interval is set, then // If new initial auto-update interval is set, then
// we should reset time that remains to the next auto-update. // we should reset time that remains to the next auto-update.
@ -134,9 +123,13 @@ void Feed::updateCounts(bool including_total_count) {
} }
void Feed::run() { void Feed::run() {
qDebug().nospace() << "Updating feed " << customId() << " in thread: \'" << QThread::currentThreadId() << "\'."; qDebug().nospace() << "Downloading new messages for feed "
<< customId() << " in thread: \'"
<< QThread::currentThreadId() << "\'.";
emit updated(update()); QList<Message> msgs = obtainNewMessages();
emit messagesObtained(msgs);
} }
int Feed::updateMessages(const QList<Message> &messages) { int Feed::updateMessages(const QList<Message> &messages) {
@ -144,13 +137,6 @@ int Feed::updateMessages(const QList<Message> &messages) {
int account_id = getParentServiceRoot()->accountId(); int account_id = getParentServiceRoot()->accountId();
bool anything_updated = false; bool anything_updated = false;
bool ok; bool ok;
// MySQL seems to be more error prone with transactions when called
// from more threads in the same time. SQLite does not have that limitation.
if (qApp->database()->activeDatabaseDriver() == DatabaseFactory::MYSQL) {
qApp->messageUpdateLock()->lock();
}
QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
int updated_messages = DatabaseQueries::updateMessages(database, messages, custom_id, account_id, url(), int updated_messages = DatabaseQueries::updateMessages(database, messages, custom_id, account_id, url(),
&anything_updated, &ok); &anything_updated, &ok);
@ -176,9 +162,5 @@ int Feed::updateMessages(const QList<Message> &messages) {
getParentServiceRoot()->itemChanged(items_to_update); getParentServiceRoot()->itemChanged(items_to_update);
} }
if (qApp->database()->activeDatabaseDriver() == DatabaseFactory::MYSQL) {
qApp->messageUpdateLock()->unlock();
}
return updated_messages; return updated_messages;
} }

View file

@ -61,13 +61,6 @@ class Feed : public RootItem, public QRunnable {
void setCountOfAllMessages(int count_all_messages); void setCountOfAllMessages(int count_all_messages);
void setCountOfUnreadMessages(int count_unread_messages); void setCountOfUnreadMessages(int count_unread_messages);
// Performs synchronous update and returns number of newly updated messages.
// NOTE: This is called from worker thread, not from main UI thread.
// NOTE: This should COMPLETELY download ALL messages from online source
// into locale "Messages" table, INCLUDING contents (or excerpts) of those
// messages.
int update();
QVariant data(int column, int role) const; QVariant data(int column, int role) const;
int autoUpdateInitialInterval() const; int autoUpdateInitialInterval() const;
@ -95,19 +88,18 @@ class Feed : public RootItem, public QRunnable {
m_url = url; m_url = url;
} }
int updateMessages(const QList<Message> &messages);
void updateCounts(bool including_total_count); void updateCounts(bool including_total_count);
// Runs update in thread (thread pooled). // Runs update in thread (thread pooled).
void run(); void run();
protected: private:
// Performs synchronous obtaining of new messages for this feed.
virtual QList<Message> obtainNewMessages() = 0; virtual QList<Message> obtainNewMessages() = 0;
private:
int updateMessages(const QList<Message> &messages);
signals: signals:
void updated(int updated_messages); void messagesObtained(QList<Message> messages);
private: private:
QString m_url; QString m_url;