Merge lp:~marcustomlinson/unity-scopes-shell/lp-1567429 into lp:unity-scopes-shell
- lp-1567429
- Merge into trunk
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 | ||||
Related bugs: |
|
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()
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:304
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
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-
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-
> 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()
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
- the destructor of Scope needs to wait for m_childScopesFu
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:305
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
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
> 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_childScopesFu
> cancel() first (according to QFuture docs, the destructor of QFuture doesn't
> wait for it to finish). Alternatively use QFutureSynchron
Very true. k, done.
Paweł Stołowski (stolowski) wrote : | # |
Ok, looks good now, thanks!
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:306
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:308
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Albert Astals Cid (aacid) wrote : | # |
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:309
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
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
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:311
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:312
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 313. By Marcus Tomlinson
-
Remove unnecessary sync() call
- 314. By Marcus Tomlinson
-
Don't emit settingsChanged() unless settings actually change
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:314
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 315. By Marcus Tomlinson
-
Emit settingsChanged when child scope state changes
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:315
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'src/Unity/scope.cpp' |
2 | --- src/Unity/scope.cpp 2016-03-17 22:35:44 +0000 |
3 | +++ src/Unity/scope.cpp 2016-04-19 08:05:18 +0000 |
4 | @@ -91,6 +91,7 @@ |
5 | , m_hasNavigation(false) |
6 | , m_favorite(false) |
7 | , m_initialQueryDone(false) |
8 | + , m_childScopesDirty(true) |
9 | , m_searchController(new CollectionController) |
10 | , m_activationController(new CollectionController) |
11 | , m_status(Status::Okay) |
12 | @@ -125,6 +126,7 @@ |
13 | |
14 | Scope::~Scope() |
15 | { |
16 | + m_childScopesFuture.waitForFinished(); |
17 | } |
18 | |
19 | void Scope::processSearchChunk(PushEvent* pushEvent) |
20 | @@ -247,6 +249,7 @@ |
21 | { |
22 | // refresh Settings view if needed |
23 | if (require_child_scopes_refresh()) { |
24 | + m_childScopesDirty = true; |
25 | update_child_scopes(); |
26 | } |
27 | |
28 | @@ -721,6 +724,10 @@ |
29 | |
30 | setSearchInProgress(true); |
31 | |
32 | + // If applicable, update this scope's child scopes now, as part of the search process |
33 | + // (i.e. while the loading bar is visible). |
34 | + update_child_scopes(); |
35 | + |
36 | if (m_proxy) { |
37 | scopes::SearchMetadata meta(QLocale::system().name().toStdString(), m_formFactor.toStdString()); |
38 | auto const userAgent = m_scopesInstance->userAgentString(); |
39 | @@ -888,10 +895,6 @@ |
40 | |
41 | unity::shell::scopes::SettingsModelInterface* Scope::settings() const |
42 | { |
43 | - if (m_settingsModel && m_scopesInstance) |
44 | - { |
45 | - m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata()); |
46 | - } |
47 | return m_settingsModel.data(); |
48 | } |
49 | |
50 | @@ -906,9 +909,20 @@ |
51 | |
52 | void Scope::update_child_scopes() |
53 | { |
54 | - if (m_settingsModel && m_scopesInstance) |
55 | + // only run the update if child scopes have changed |
56 | + if (m_childScopesDirty && m_settingsModel && m_scopesInstance) |
57 | { |
58 | - m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata()); |
59 | + // reset the flag so that re-entering this method won't restart the update unnecessarily |
60 | + m_childScopesDirty = false; |
61 | + |
62 | + // just in case we have another update still running, wait here for it to complete |
63 | + m_childScopesFuture.waitForFinished(); |
64 | + |
65 | + // run the update in a seperate thread |
66 | + m_childScopesFuture = QtConcurrent::run([this] |
67 | + { |
68 | + m_settingsModel->update_child_scopes(m_scopesInstance->getAllMetadata()); |
69 | + }); |
70 | } |
71 | } |
72 | |
73 | @@ -1258,6 +1272,11 @@ |
74 | m_activationController->invalidate(); |
75 | } |
76 | |
77 | +void Scope::invalidateChildScopes() |
78 | +{ |
79 | + m_childScopesDirty = true; |
80 | +} |
81 | + |
82 | void Scope::invalidateResults() |
83 | { |
84 | if (m_isActive) { |
85 | |
86 | === modified file 'src/Unity/scope.h' |
87 | --- src/Unity/scope.h 2016-03-07 08:29:14 +0000 |
88 | +++ src/Unity/scope.h 2016-04-19 08:05:18 +0000 |
89 | @@ -21,6 +21,7 @@ |
90 | #define NG_SCOPE_H |
91 | |
92 | // Qt |
93 | +#include <QFuture> |
94 | #include <QObject> |
95 | #include <QString> |
96 | #include <QTimer> |
97 | @@ -180,6 +181,7 @@ |
98 | void setSearchQueryString(const QString& search_query); |
99 | |
100 | public Q_SLOTS: |
101 | + void invalidateChildScopes(); |
102 | void invalidateResults(); |
103 | virtual void dispatchSearch(); |
104 | void setSearchInProgress(bool searchInProgress); |
105 | @@ -248,6 +250,9 @@ |
106 | bool m_favorite; |
107 | bool m_initialQueryDone; |
108 | |
109 | + bool m_childScopesDirty; |
110 | + QFuture<void> m_childScopesFuture; |
111 | + |
112 | QMap<std::string, QList<std::shared_ptr<unity::scopes::CategorisedResult>>> m_category_results; |
113 | std::unique_ptr<CollectionController> m_searchController; |
114 | std::unique_ptr<CollectionController> m_activationController; |
115 | |
116 | === modified file 'src/Unity/scopes.cpp' |
117 | --- src/Unity/scopes.cpp 2016-02-04 11:43:34 +0000 |
118 | +++ src/Unity/scopes.cpp 2016-04-19 08:05:18 +0000 |
119 | @@ -550,10 +550,12 @@ |
120 | qDebug() << "Refreshing scope metadata"; |
121 | refreshScopeMetadata(); |
122 | Q_FOREACH(Scope::Ptr scope, m_scopes) { |
123 | + scope->invalidateChildScopes(); |
124 | scope->invalidateResults(); |
125 | } |
126 | |
127 | Q_FOREACH(Scope::Ptr scope, m_tempScopes) { |
128 | + scope->invalidateChildScopes(); |
129 | scope->invalidateResults(); |
130 | } |
131 | } |
132 | |
133 | === modified file 'src/Unity/settingsmodel.cpp' |
134 | --- src/Unity/settingsmodel.cpp 2016-03-30 12:34:12 +0000 |
135 | +++ src/Unity/settingsmodel.cpp 2016-04-19 08:05:18 +0000 |
136 | @@ -152,6 +152,8 @@ |
137 | |
138 | QVariant SettingsModel::data(const QModelIndex& index, int role) const |
139 | { |
140 | + QMutexLocker locker(&m_mutex); |
141 | + |
142 | int row = index.row(); |
143 | QVariant result; |
144 | |
145 | @@ -247,6 +249,8 @@ |
146 | |
147 | QVariant SettingsModel::value(const QString& id) const |
148 | { |
149 | + QMutexLocker locker(&m_mutex); |
150 | + |
151 | // Check for the setting id in the child scopes list first, in case the |
152 | // aggregator is incorrectly using a scope id as a settings as well. |
153 | if (m_child_scopes_data_by_id.contains(id)) |
154 | @@ -303,6 +307,8 @@ |
155 | |
156 | void SettingsModel::update_child_scopes(QMap<QString, sc::ScopeMetadata::SPtr> const& scopes_metadata) |
157 | { |
158 | + QMutexLocker locker(&m_mutex); |
159 | + |
160 | if (!scopes_metadata.contains(m_scopeId) || |
161 | !scopes_metadata[m_scopeId]->is_aggregator()) |
162 | { |
163 | @@ -346,15 +352,6 @@ |
164 | } |
165 | QString displayName = QString::fromStdString(_("Display results from %1")).arg(QString(scopes_metadata[id]->display_name().c_str())); |
166 | |
167 | - QSharedPointer<QTimer> timer(new QTimer()); |
168 | - timer->setProperty("setting_id", id); |
169 | - timer->setSingleShot(true); |
170 | - timer->setInterval(m_settingsTimeout); |
171 | - timer->setTimerType(Qt::VeryCoarseTimer); |
172 | - connect(timer.data(), SIGNAL(timeout()), this, |
173 | - SLOT(settings_timeout())); |
174 | - m_child_scopes_timers[id] = timer; |
175 | - |
176 | QSharedPointer<Data> setting( |
177 | new Data(id, displayName, QStringLiteral("boolean"), QVariantMap(), QVariant(), QVariant::Bool)); |
178 | |
179 | @@ -364,13 +361,17 @@ |
180 | |
181 | if (reset) { |
182 | endResetModel(); |
183 | - Q_EMIT countChanged(); |
184 | } |
185 | + |
186 | + locker.unlock(); |
187 | + Q_EMIT countChanged(); |
188 | } |
189 | |
190 | bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, |
191 | int role) |
192 | { |
193 | + QMutexLocker locker(&m_mutex); |
194 | + |
195 | int row = index.row(); |
196 | QVariant result; |
197 | |
198 | @@ -400,6 +401,18 @@ |
199 | { |
200 | case Roles::RoleValue: |
201 | { |
202 | + if (!m_child_scopes_timers.contains(data->id)) |
203 | + { |
204 | + QSharedPointer<QTimer> timer(new QTimer()); |
205 | + timer->setProperty("setting_id", data->id); |
206 | + timer->setSingleShot(true); |
207 | + timer->setInterval(m_settingsTimeout); |
208 | + timer->setTimerType(Qt::VeryCoarseTimer); |
209 | + connect(timer.data(), SIGNAL(timeout()), this, |
210 | + SLOT(settings_timeout())); |
211 | + m_child_scopes_timers[data->id] = timer; |
212 | + } |
213 | + |
214 | QSharedPointer<QTimer> timer = m_child_scopes_timers[data->id]; |
215 | timer->setProperty("index", row - m_data.size()); |
216 | timer->setProperty("value", value); |
217 | @@ -422,16 +435,20 @@ |
218 | |
219 | int SettingsModel::count() const |
220 | { |
221 | + QMutexLocker locker(&m_mutex); |
222 | return m_data.size() + m_child_scopes_data.size(); |
223 | } |
224 | |
225 | bool SettingsModel::require_child_scopes_refresh() const |
226 | { |
227 | + QMutexLocker locker(&m_mutex); |
228 | return m_requireChildScopesRefresh; |
229 | } |
230 | |
231 | void SettingsModel::settings_timeout() |
232 | { |
233 | + QMutexLocker locker(&m_mutex); |
234 | + |
235 | QObject *timer = sender(); |
236 | if (!timer) |
237 | { |
238 | @@ -454,6 +471,9 @@ |
239 | try |
240 | { |
241 | m_scopeProxy->set_child_scopes(m_child_scopes); |
242 | + |
243 | + locker.unlock(); |
244 | + Q_EMIT settingsChanged(); |
245 | } |
246 | catch (std::exception const& e) |
247 | { |
248 | @@ -488,6 +508,7 @@ |
249 | FileLock lock = unixLock(m_settings_path, true); |
250 | m_settings->sync(); // make sure the change to setting value is synced to fs |
251 | |
252 | + locker.unlock(); |
253 | Q_EMIT settingsChanged(); |
254 | } |
255 | catch(const unity::FileException& e) |
256 | |
257 | === modified file 'src/Unity/settingsmodel.h' |
258 | --- src/Unity/settingsmodel.h 2016-03-30 12:34:12 +0000 |
259 | +++ src/Unity/settingsmodel.h 2016-04-19 08:05:18 +0000 |
260 | @@ -29,6 +29,7 @@ |
261 | |
262 | #include <QAbstractListModel> |
263 | #include <QList> |
264 | +#include <QMutex> |
265 | #include <QSharedPointer> |
266 | |
267 | QT_BEGIN_NAMESPACE |
268 | @@ -93,6 +94,7 @@ |
269 | void tryLoadSettings() const; |
270 | |
271 | protected: |
272 | + mutable QMutex m_mutex; |
273 | QString m_scopeId; |
274 | unity::scopes::ScopeProxy m_scopeProxy; |
275 | int m_settingsTimeout; |
FAILED: Continuous integration, rev:302 jenkins. qa.ubuntu. com/job/ unity-scopes- shell-ci/ 433/ jenkins. qa.ubuntu. com/job/ unity-scopes- shell-vivid- amd64-ci/ 127 jenkins. qa.ubuntu. com/job/ unity-scopes- shell-vivid- armhf-ci/ 127 jenkins. qa.ubuntu. com/job/ unity-scopes- shell-vivid- armhf-ci/ 127/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity-scopes- shell-vivid- i386-ci/ 127 jenkins. qa.ubuntu. com/job/ unity-scopes- shell-wily- amd64-ci/ 90/console jenkins. qa.ubuntu. com/job/ unity-scopes- shell-wily- armhf-ci/ 90/console jenkins. qa.ubuntu. com/job/ unity-scopes- shell-wily- i386-ci/ 90/console
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scopes- shell-ci/ 433/rebuild
http://