Merge lp:~marcustomlinson/unity-scopes-shell/lp-1567429 into lp:unity-scopes-shell

Proposed by Marcus Tomlinson
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 315
Merged at revision: 313
Proposed branch: lp:~marcustomlinson/unity-scopes-shell/lp-1567429
Merge into: lp:unity-scopes-shell
Diff against target: 275 lines (+65/-16)
5 files modified
src/Unity/scope.cpp (+25/-6)
src/Unity/scope.h (+5/-0)
src/Unity/scopes.cpp (+2/-0)
src/Unity/settingsmodel.cpp (+31/-10)
src/Unity/settingsmodel.h (+2/-0)
To merge this branch: bzr merge lp:~marcustomlinson/unity-scopes-shell/lp-1567429
Reviewer Review Type Date Requested Status
Paweł Stołowski (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+291480@code.launchpad.net

Commit message

Update child scopes in a background thread to greatly improve the performance of Scope::settings()

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Looks good, I think it will improve the perceived performance of Settings page quite a bit already. I yet need to check the silo and how it impacts search.

I think there is one other potential improvement we could make though: make the call to m_scopeProxy->child_scopes() non-blocking. I think currently since it blocks the main thread we may see the loading bar animation struggling during search. What do you think about starting the call in a separate thread and *only* waiting for the call to finish when the settings are actually accessed/needed (e.g. in call to data() if there is no better place)?

review: Needs Information
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> Looks good, I think it will improve the perceived performance of Settings page
> quite a bit already. I yet need to check the silo and how it impacts search.
>
> I think there is one other potential improvement we could make though: make
> the call to m_scopeProxy->child_scopes() non-blocking. I think currently since
> it blocks the main thread we may see the loading bar animation struggling
> during search. What do you think about starting the call in a separate thread
> and *only* waiting for the call to finish when the settings are actually
> accessed/needed (e.g. in call to data() if there is no better place)?

Good point. Ok I've moved the child scopes update to a background thread, expected to complete upon a call to settings()

Revision history for this message
Paweł Stołowski (stolowski) wrote :

Thanks, great stuff with QFuture! This looks very good, but I think with these changes we need to address the following now:

- m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata()) is now called from a new thread, so m_settingsModel needs to synchronize access to its members. We may have a situation where Settings page is already opened by the UI and registry change triggers the update of child scopes at the same time.
- the destructor of Scope needs to wait for m_childScopesFuture, possibly with cancel() first (according to QFuture docs, the destructor of QFuture doesn't wait for it to finish). Alternatively use QFutureSynchronizer.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> Thanks, great stuff with QFuture! This looks very good, but I think with these
> changes we need to address the following now:
>
> - m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata()) is
> now called from a new thread, so m_settingsModel needs to synchronize access
> to its members. We may have a situation where Settings page is already opened
> by the UI and registry change triggers the update of child scopes at the same
> time.
> - the destructor of Scope needs to wait for m_childScopesFuture, possibly with
> cancel() first (according to QFuture docs, the destructor of QFuture doesn't
> wait for it to finish). Alternatively use QFutureSynchronizer.

Very true. k, done.

Revision history for this message
Paweł Stołowski (stolowski) wrote :

Ok, looks good now, thanks!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

http://paste.ubuntu.com/15789165/ has the outcome of this branch, an improvement for sure.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

New patch brigns down the thing to 0msec now, seems much nicer and can't see it regressing in any obvious way

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
313. By Marcus Tomlinson

Remove unnecessary sync() call

314. By Marcus Tomlinson

Don't emit settingsChanged() unless settings actually change

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
315. By Marcus Tomlinson

Emit settingsChanged when child scope state changes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/Unity/scope.cpp'
--- src/Unity/scope.cpp 2016-03-17 22:35:44 +0000
+++ src/Unity/scope.cpp 2016-04-19 08:05:18 +0000
@@ -91,6 +91,7 @@
91 , m_hasNavigation(false)91 , m_hasNavigation(false)
92 , m_favorite(false)92 , m_favorite(false)
93 , m_initialQueryDone(false)93 , m_initialQueryDone(false)
94 , m_childScopesDirty(true)
94 , m_searchController(new CollectionController)95 , m_searchController(new CollectionController)
95 , m_activationController(new CollectionController)96 , m_activationController(new CollectionController)
96 , m_status(Status::Okay)97 , m_status(Status::Okay)
@@ -125,6 +126,7 @@
125126
126Scope::~Scope()127Scope::~Scope()
127{128{
129 m_childScopesFuture.waitForFinished();
128}130}
129131
130void Scope::processSearchChunk(PushEvent* pushEvent)132void Scope::processSearchChunk(PushEvent* pushEvent)
@@ -247,6 +249,7 @@
247{249{
248 // refresh Settings view if needed250 // refresh Settings view if needed
249 if (require_child_scopes_refresh()) {251 if (require_child_scopes_refresh()) {
252 m_childScopesDirty = true;
250 update_child_scopes();253 update_child_scopes();
251 }254 }
252255
@@ -721,6 +724,10 @@
721724
722 setSearchInProgress(true);725 setSearchInProgress(true);
723726
727 // If applicable, update this scope's child scopes now, as part of the search process
728 // (i.e. while the loading bar is visible).
729 update_child_scopes();
730
724 if (m_proxy) {731 if (m_proxy) {
725 scopes::SearchMetadata meta(QLocale::system().name().toStdString(), m_formFactor.toStdString());732 scopes::SearchMetadata meta(QLocale::system().name().toStdString(), m_formFactor.toStdString());
726 auto const userAgent = m_scopesInstance->userAgentString();733 auto const userAgent = m_scopesInstance->userAgentString();
@@ -888,10 +895,6 @@
888895
889unity::shell::scopes::SettingsModelInterface* Scope::settings() const896unity::shell::scopes::SettingsModelInterface* Scope::settings() const
890{897{
891 if (m_settingsModel && m_scopesInstance)
892 {
893 m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata());
894 }
895 return m_settingsModel.data();898 return m_settingsModel.data();
896}899}
897900
@@ -906,9 +909,20 @@
906909
907void Scope::update_child_scopes()910void Scope::update_child_scopes()
908{911{
909 if (m_settingsModel && m_scopesInstance)912 // only run the update if child scopes have changed
913 if (m_childScopesDirty && m_settingsModel && m_scopesInstance)
910 {914 {
911 m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata());915 // reset the flag so that re-entering this method won't restart the update unnecessarily
916 m_childScopesDirty = false;
917
918 // just in case we have another update still running, wait here for it to complete
919 m_childScopesFuture.waitForFinished();
920
921 // run the update in a seperate thread
922 m_childScopesFuture = QtConcurrent::run([this]
923 {
924 m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata());
925 });
912 }926 }
913}927}
914928
@@ -1258,6 +1272,11 @@
1258 m_activationController->invalidate();1272 m_activationController->invalidate();
1259}1273}
12601274
1275void Scope::invalidateChildScopes()
1276{
1277 m_childScopesDirty = true;
1278}
1279
1261void Scope::invalidateResults()1280void Scope::invalidateResults()
1262{1281{
1263 if (m_isActive) {1282 if (m_isActive) {
12641283
=== modified file 'src/Unity/scope.h'
--- src/Unity/scope.h 2016-03-07 08:29:14 +0000
+++ src/Unity/scope.h 2016-04-19 08:05:18 +0000
@@ -21,6 +21,7 @@
21#define NG_SCOPE_H21#define NG_SCOPE_H
2222
23// Qt23// Qt
24#include <QFuture>
24#include <QObject>25#include <QObject>
25#include <QString>26#include <QString>
26#include <QTimer>27#include <QTimer>
@@ -180,6 +181,7 @@
180 void setSearchQueryString(const QString& search_query);181 void setSearchQueryString(const QString& search_query);
181182
182public Q_SLOTS:183public Q_SLOTS:
184 void invalidateChildScopes();
183 void invalidateResults();185 void invalidateResults();
184 virtual void dispatchSearch();186 virtual void dispatchSearch();
185 void setSearchInProgress(bool searchInProgress);187 void setSearchInProgress(bool searchInProgress);
@@ -248,6 +250,9 @@
248 bool m_favorite;250 bool m_favorite;
249 bool m_initialQueryDone;251 bool m_initialQueryDone;
250252
253 bool m_childScopesDirty;
254 QFuture<void> m_childScopesFuture;
255
251 QMap<std::string, QList<std::shared_ptr<unity::scopes::CategorisedResult>>> m_category_results;256 QMap<std::string, QList<std::shared_ptr<unity::scopes::CategorisedResult>>> m_category_results;
252 std::unique_ptr<CollectionController> m_searchController;257 std::unique_ptr<CollectionController> m_searchController;
253 std::unique_ptr<CollectionController> m_activationController;258 std::unique_ptr<CollectionController> m_activationController;
254259
=== modified file 'src/Unity/scopes.cpp'
--- src/Unity/scopes.cpp 2016-02-04 11:43:34 +0000
+++ src/Unity/scopes.cpp 2016-04-19 08:05:18 +0000
@@ -550,10 +550,12 @@
550 qDebug() << "Refreshing scope metadata";550 qDebug() << "Refreshing scope metadata";
551 refreshScopeMetadata();551 refreshScopeMetadata();
552 Q_FOREACH(Scope::Ptr scope, m_scopes) {552 Q_FOREACH(Scope::Ptr scope, m_scopes) {
553 scope->invalidateChildScopes();
553 scope->invalidateResults();554 scope->invalidateResults();
554 }555 }
555556
556 Q_FOREACH(Scope::Ptr scope, m_tempScopes) {557 Q_FOREACH(Scope::Ptr scope, m_tempScopes) {
558 scope->invalidateChildScopes();
557 scope->invalidateResults();559 scope->invalidateResults();
558 }560 }
559}561}
560562
=== modified file 'src/Unity/settingsmodel.cpp'
--- src/Unity/settingsmodel.cpp 2016-03-30 12:34:12 +0000
+++ src/Unity/settingsmodel.cpp 2016-04-19 08:05:18 +0000
@@ -152,6 +152,8 @@
152152
153QVariant SettingsModel::data(const QModelIndex& index, int role) const153QVariant SettingsModel::data(const QModelIndex& index, int role) const
154{154{
155 QMutexLocker locker(&m_mutex);
156
155 int row = index.row();157 int row = index.row();
156 QVariant result;158 QVariant result;
157159
@@ -247,6 +249,8 @@
247249
248QVariant SettingsModel::value(const QString& id) const250QVariant SettingsModel::value(const QString& id) const
249{251{
252 QMutexLocker locker(&m_mutex);
253
250 // Check for the setting id in the child scopes list first, in case the254 // Check for the setting id in the child scopes list first, in case the
251 // aggregator is incorrectly using a scope id as a settings as well.255 // aggregator is incorrectly using a scope id as a settings as well.
252 if (m_child_scopes_data_by_id.contains(id))256 if (m_child_scopes_data_by_id.contains(id))
@@ -303,6 +307,8 @@
303307
304void SettingsModel::update_child_scopes(QMap<QString, sc::ScopeMetadata::SPtr> const& scopes_metadata)308void SettingsModel::update_child_scopes(QMap<QString, sc::ScopeMetadata::SPtr> const& scopes_metadata)
305{309{
310 QMutexLocker locker(&m_mutex);
311
306 if (!scopes_metadata.contains(m_scopeId) ||312 if (!scopes_metadata.contains(m_scopeId) ||
307 !scopes_metadata[m_scopeId]->is_aggregator())313 !scopes_metadata[m_scopeId]->is_aggregator())
308 {314 {
@@ -346,15 +352,6 @@
346 }352 }
347 QString displayName = QString::fromStdString(_("Display results from %1")).arg(QString(scopes_metadata[id]->display_name().c_str()));353 QString displayName = QString::fromStdString(_("Display results from %1")).arg(QString(scopes_metadata[id]->display_name().c_str()));
348354
349 QSharedPointer<QTimer> timer(new QTimer());
350 timer->setProperty("setting_id", id);
351 timer->setSingleShot(true);
352 timer->setInterval(m_settingsTimeout);
353 timer->setTimerType(Qt::VeryCoarseTimer);
354 connect(timer.data(), SIGNAL(timeout()), this,
355 SLOT(settings_timeout()));
356 m_child_scopes_timers[id] = timer;
357
358 QSharedPointer<Data> setting(355 QSharedPointer<Data> setting(
359 new Data(id, displayName, QStringLiteral("boolean"), QVariantMap(), QVariant(), QVariant::Bool));356 new Data(id, displayName, QStringLiteral("boolean"), QVariantMap(), QVariant(), QVariant::Bool));
360357
@@ -364,13 +361,17 @@
364361
365 if (reset) {362 if (reset) {
366 endResetModel();363 endResetModel();
367 Q_EMIT countChanged();
368 }364 }
365
366 locker.unlock();
367 Q_EMIT countChanged();
369}368}
370369
371bool SettingsModel::setData(const QModelIndex &index, const QVariant &value,370bool SettingsModel::setData(const QModelIndex &index, const QVariant &value,
372 int role)371 int role)
373{372{
373 QMutexLocker locker(&m_mutex);
374
374 int row = index.row();375 int row = index.row();
375 QVariant result;376 QVariant result;
376377
@@ -400,6 +401,18 @@
400 {401 {
401 case Roles::RoleValue:402 case Roles::RoleValue:
402 {403 {
404 if (!m_child_scopes_timers.contains(data->id))
405 {
406 QSharedPointer<QTimer> timer(new QTimer());
407 timer->setProperty("setting_id", data->id);
408 timer->setSingleShot(true);
409 timer->setInterval(m_settingsTimeout);
410 timer->setTimerType(Qt::VeryCoarseTimer);
411 connect(timer.data(), SIGNAL(timeout()), this,
412 SLOT(settings_timeout()));
413 m_child_scopes_timers[data->id] = timer;
414 }
415
403 QSharedPointer<QTimer> timer = m_child_scopes_timers[data->id];416 QSharedPointer<QTimer> timer = m_child_scopes_timers[data->id];
404 timer->setProperty("index", row - m_data.size());417 timer->setProperty("index", row - m_data.size());
405 timer->setProperty("value", value);418 timer->setProperty("value", value);
@@ -422,16 +435,20 @@
422435
423int SettingsModel::count() const436int SettingsModel::count() const
424{437{
438 QMutexLocker locker(&m_mutex);
425 return m_data.size() + m_child_scopes_data.size();439 return m_data.size() + m_child_scopes_data.size();
426}440}
427441
428bool SettingsModel::require_child_scopes_refresh() const442bool SettingsModel::require_child_scopes_refresh() const
429{443{
444 QMutexLocker locker(&m_mutex);
430 return m_requireChildScopesRefresh;445 return m_requireChildScopesRefresh;
431}446}
432447
433void SettingsModel::settings_timeout()448void SettingsModel::settings_timeout()
434{449{
450 QMutexLocker locker(&m_mutex);
451
435 QObject *timer = sender();452 QObject *timer = sender();
436 if (!timer)453 if (!timer)
437 {454 {
@@ -454,6 +471,9 @@
454 try471 try
455 {472 {
456 m_scopeProxy->set_child_scopes(m_child_scopes);473 m_scopeProxy->set_child_scopes(m_child_scopes);
474
475 locker.unlock();
476 Q_EMIT settingsChanged();
457 }477 }
458 catch (std::exception const& e)478 catch (std::exception const& e)
459 {479 {
@@ -488,6 +508,7 @@
488 FileLock lock = unixLock(m_settings_path, true);508 FileLock lock = unixLock(m_settings_path, true);
489 m_settings->sync(); // make sure the change to setting value is synced to fs509 m_settings->sync(); // make sure the change to setting value is synced to fs
490510
511 locker.unlock();
491 Q_EMIT settingsChanged();512 Q_EMIT settingsChanged();
492 }513 }
493 catch(const unity::FileException& e)514 catch(const unity::FileException& e)
494515
=== modified file 'src/Unity/settingsmodel.h'
--- src/Unity/settingsmodel.h 2016-03-30 12:34:12 +0000
+++ src/Unity/settingsmodel.h 2016-04-19 08:05:18 +0000
@@ -29,6 +29,7 @@
2929
30#include <QAbstractListModel>30#include <QAbstractListModel>
31#include <QList>31#include <QList>
32#include <QMutex>
32#include <QSharedPointer>33#include <QSharedPointer>
3334
34QT_BEGIN_NAMESPACE35QT_BEGIN_NAMESPACE
@@ -93,6 +94,7 @@
93 void tryLoadSettings() const;94 void tryLoadSettings() const;
9495
95protected:96protected:
97 mutable QMutex m_mutex;
96 QString m_scopeId;98 QString m_scopeId;
97 unity::scopes::ScopeProxy m_scopeProxy;99 unity::scopes::ScopeProxy m_scopeProxy;
98 int m_settingsTimeout;100 int m_settingsTimeout;

Subscribers

People subscribed via source and target branches

to all changes: