Working on safer msg downloading/saving.
This commit is contained in:
parent
a259cba741
commit
d9311ae5d2
10 changed files with 44 additions and 64 deletions
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,8 +113,8 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.",
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue