Merge lp:~mhr3/libunity/shared-models-only-on-main-thread into lp:libunity

Proposed by Michal Hruby
Status: Merged
Approved by: James Henstridge
Approved revision: 261
Merged at revision: 260
Proposed branch: lp:~mhr3/libunity/shared-models-only-on-main-thread
Merge into: lp:libunity
Diff against target: 300 lines (+117/-43)
6 files modified
configure.ac (+1/-1)
src/unity-aggregator-scope-private.vala (+1/-2)
src/unity-deprecated-scope-impl.vala (+5/-6)
src/unity-models.vala (+71/-14)
src/unity-scope-dbus-impl.vala (+6/-6)
test/vala/test-scope.vala (+33/-14)
To merge this branch: bzr merge lp:~mhr3/libunity/shared-models-only-on-main-thread
Reviewer Review Type Date Requested Status
James Henstridge Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+175317@code.launchpad.net

Commit message

Fix a threading issue where shared model could have been serialized (in response to a standard Clone() request defined by the dee protocol) while a search was running in a separate thread and adding more rows to the model.

Description of the change

Fix a threading issue where shared model could have been serialized (in response to a standard Clone() request defined by the dee protocol) while a search was running in a separate thread add adding more rows to the model.

DeeSharedModels are no longer pushed to directly from the search thread, instead a IdleSource is used to add the rows from the main thread. This only fixes the non-deprecated scopes, but the deprecated ones don't perform searches in a separate thread in the first place.

To post a comment you must log in.
260. By Michal Hruby

Use more appropriate name in the tests

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
261. By Michal Hruby

Bump valac requirement

Revision history for this message
Marco Trevisan (TreviƱo) (3v1n0) wrote :

Probably changing also build-dep for valac?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
James Henstridge (jamesh) wrote :

Looks good. I notice that it slightly changes the search time metric, but it probably won't make a substantial difference.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.ac'
2--- configure.ac 2013-07-11 10:42:15 +0000
3+++ configure.ac 2013-07-17 17:22:24 +0000
4@@ -63,7 +63,7 @@
5 AM_PROG_CC_STDC
6 AC_HEADER_STDC
7 AM_PROG_LIBTOOL
8-AM_PROG_VALAC([0.16.0])
9+AM_PROG_VALAC([0.18.0])
10 AS_IF([test -z "$VALAC"], [AC_MSG_ERROR(["Can't find suitable vala compiler."])])
11
12 ###################################################################
13
14=== modified file 'src/unity-aggregator-scope-private.vala'
15--- src/unity-aggregator-scope-private.vala 2013-07-16 17:23:39 +0000
16+++ src/unity-aggregator-scope-private.vala 2013-07-17 17:22:24 +0000
17@@ -1621,6 +1621,7 @@
18
19 Unity.Trace.tracepoint ("%s run end: %s", Log.METHOD, owner.id);
20
21+ result_set.flush ();
22 response[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
23 if (measure_requests)
24 {
25@@ -1629,8 +1630,6 @@
26 response[SEARCH_TIME_HINT] = new Variant.double (delta);
27 }
28
29- result_set.flush ();
30-
31 if (cancellable != null) cancellable.set_error_if_cancelled ();
32 return response;
33 }
34
35=== modified file 'src/unity-deprecated-scope-impl.vala'
36--- src/unity-deprecated-scope-impl.vala 2013-07-16 17:23:39 +0000
37+++ src/unity-deprecated-scope-impl.vala 2013-07-17 17:22:24 +0000
38@@ -503,7 +503,11 @@
39 });
40 yield;
41
42- result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
43+ if (!cancellable.is_cancelled ())
44+ {
45+ result_set.flush ();
46+ result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
47+ }
48
49 Unity.Trace.tracepoint ("%s run end: %s", Log.METHOD, owner.id);
50 if (measure_requests)
51@@ -513,11 +517,6 @@
52 result[SEARCH_TIME_HINT] = new Variant.double (delta);
53 }
54
55- if (!cancellable.is_cancelled ())
56- {
57- result_set.flush ();
58- }
59-
60 // handle hints
61 var reply_hints = new_search.get_reply_hints ();
62 if (reply_hints != null)
63
64=== modified file 'src/unity-models.vala'
65--- src/unity-models.vala 2013-07-15 09:56:14 +0000
66+++ src/unity-models.vala 2013-07-17 17:22:24 +0000
67@@ -35,6 +35,18 @@
68 Object (results_model: model);
69 }
70
71+ private unowned Thread<void*> origin_thread;
72+ private unowned MainContext origin_context;
73+ private GenericArray<Variant> results;
74+
75+ construct
76+ {
77+ origin_thread = Thread.self<void*> ();
78+ origin_context = MainContext.get_thread_default ();
79+ if (origin_context == null) origin_context = MainContext.default ();
80+ results = new GenericArray<Variant> ();
81+ }
82+
83 public override void constructed ()
84 {
85 if (results_model == null)
86@@ -59,33 +71,46 @@
87 {
88 metadata_v = new Variant.array (VariantType.VARDICT.element (), {});
89 }
90- results_model.append (result.uri, result.icon_hint, result.category,
91- result.result_type, result.mimetype, result.title,
92- result.comment, result.dnd_uri, metadata_v);
93+
94+ Variant v = new Variant ("(ssuussss@a{sv})", result.uri, result.icon_hint,
95+ result.category, result.result_type, result.mimetype, result.title,
96+ result.comment, result.dnd_uri, metadata_v);
97+ results.add ((owned) v);
98 }
99
100 public override void add_result_from_variant (Variant variant)
101 {
102- if (variant.get_type_string () != "(ssuussssa{sv})")
103+ const string EXPECTED_TYPE_STRING = "(ssuussssa{sv})";
104+ if (variant.get_type_string () != EXPECTED_TYPE_STRING)
105 {
106- warning ("Incorrect signature for %s", Log.METHOD);
107+ warning ("Incorrect signature for %s, expected %s, but got %s",
108+ Log.METHOD, EXPECTED_TYPE_STRING, variant.get_type_string ());
109 return;
110 }
111
112- Variant row_buf[9];
113- variant.get ("(@s@s@u@u@s@s@s@s@a{sv})",
114- out row_buf[0], out row_buf[1],
115- out row_buf[2], out row_buf[3],
116- out row_buf[4], out row_buf[5],
117- out row_buf[6], out row_buf[7],
118- out row_buf[8]);
119- results_model.append_row (row_buf);
120+ results.add (variant);
121 }
122
123 public Dee.SerializableModel flush_model { get; set; }
124
125- public override void flush ()
126+ private void do_flush_locked ()
127 {
128+ uint results_len = (uint) results.length;
129+ for (uint i = 0; i < results_len; i++)
130+ {
131+ Variant variant = (owned) results.data[i];
132+ Variant row_buf[9];
133+ variant.get ("(@s@s@u@u@s@s@s@s@a{sv})",
134+ out row_buf[0], out row_buf[1],
135+ out row_buf[2], out row_buf[3],
136+ out row_buf[4], out row_buf[5],
137+ out row_buf[6], out row_buf[7],
138+ out row_buf[8]);
139+ results_model.append_row (row_buf);
140+ }
141+ // clear the results array
142+ results.length = 0;
143+
144 var diff_model = flush_model as DiffModel;
145 if (diff_model != null)
146 {
147@@ -97,6 +122,38 @@
148 sm.flush_revision_queue ();
149 }
150 }
151+
152+ /* Note that this method can either be called from inside the search thread
153+ * (while the search is in progress), or from the main thread BUT only after
154+ * the search finished (if search is run in main thread it's safe always). */
155+ public override void flush ()
156+ {
157+ // check which thread is this running in
158+ unowned Thread<void*> current_thread = Thread.self<void*> ();
159+ if (current_thread == origin_thread)
160+ {
161+ // no locking needed
162+ do_flush_locked ();
163+ }
164+ else
165+ {
166+ // can't use Mutex and Cond directly, vala does scary copying of structs
167+ var queue = new AsyncQueue<bool> ();
168+ var idle = new IdleSource ();
169+ idle.set_callback (() =>
170+ {
171+ do_flush_locked ();
172+
173+ queue.push (true);
174+
175+ return false;
176+ });
177+ idle.attach (origin_context);
178+
179+ // wait for the idle to get processed
180+ queue.pop ();
181+ }
182+ }
183 }
184
185 internal class DiffModel: Dee.SharedModel
186
187=== modified file 'src/unity-scope-dbus-impl.vala'
188--- src/unity-scope-dbus-impl.vala 2013-07-16 17:23:39 +0000
189+++ src/unity-scope-dbus-impl.vala 2013-07-17 17:22:24 +0000
190@@ -549,18 +549,18 @@
191
192 Unity.Trace.tracepoint ("%s run end: %s", Log.METHOD, _dbus_name);
193
194- result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
195+ // FIXME: handle no-reply-hint etc!
196+ if (!cancellable.is_cancelled ())
197+ {
198+ result_set.flush ();
199+ result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
200+ }
201 if (measure_requests)
202 {
203 int64 delta_us = search_end_time - search_start_time;
204 double delta = delta_us / 1000000.0;
205 result[SEARCH_TIME_HINT] = new Variant.double (delta);
206 }
207- // FIXME: handle no-reply-hint etc!
208- if (!cancellable.is_cancelled ())
209- {
210- result_set.flush ();
211- }
212 }
213 finally
214 {
215
216=== modified file 'test/vala/test-scope.vala'
217--- test/vala/test-scope.vala 2013-07-16 17:24:10 +0000
218+++ test/vala/test-scope.vala 2013-07-17 17:22:24 +0000
219@@ -76,6 +76,8 @@
220 Fixture.create<SimpleScopeTester> (SimpleScopeTester.test_simple_search));
221 Test.add_data_func ("/Unit/SimpleScope/SearchAsync",
222 Fixture.create<SimpleScopeTester> (SimpleScopeTester.test_simple_search_async));
223+ Test.add_data_func ("/Unit/SimpleScope/SearchWithFlush",
224+ Fixture.create<SimpleScopeTester> (SimpleScopeTester.test_simple_search_with_flush));
225 Test.add_data_func ("/Unit/SimpleScope/DiffSearch",
226 Fixture.create<SimpleScopeTester> (SimpleScopeTester.test_simple_diff_search));
227 Test.add_data_func ("/Unit/SimpleScope/NoDiffSearch",
228@@ -1015,7 +1017,31 @@
229 assert (model.get_string (iter, 0) == "test:uri");
230 }
231
232- public static void search_func (Unity.ScopeSearchBase search, int state = 0)
233+ public void test_simple_search_with_flush ()
234+ {
235+ int search_invoked = 0;
236+ // invoked in separate thread
237+ simple.set_search_func ((search) =>
238+ {
239+ search_invoked++;
240+
241+ for (int i = 0; i <= 1; i++)
242+ {
243+ add_search_result (search, i);
244+ search.search_context.result_set.flush ();
245+ }
246+ });
247+
248+ var reply_dict = perform_search ("");
249+ assert (reply_dict != null);
250+
251+ assert (search_invoked == 1);
252+ assert (model.get_n_rows () == 2);
253+ var iter = model.get_first_iter ();
254+ assert (model.get_string (iter, 0) == "test:uri");
255+ }
256+
257+ public static void add_search_result (Unity.ScopeSearchBase search, int state = 0)
258 {
259 Unity.ScopeResult result = Unity.ScopeResult ();
260 switch (state)
261@@ -1033,17 +1059,6 @@
262 search.search_context.result_set.add_result (result);
263 break;
264 case 1:
265- result.uri = "test:uri";
266- result.icon_hint = "";
267- result.category = 0;
268- result.result_type = Unity.ResultType.DEFAULT;
269- result.mimetype = "inode/folder";
270- result.title = "Title";
271- result.comment = "";
272- result.dnd_uri = "test::uri";
273- result.metadata = null;
274- search.search_context.result_set.add_result (result);
275-
276 result.uri = "test:uri2";
277 result.icon_hint = "";
278 result.category = 0;
279@@ -1064,7 +1079,9 @@
280 int search_state = 1;
281 simple.set_search_func ((search) =>
282 {
283- search_func (search, search_state);
284+ add_search_result (search, 0);
285+ if (search_state == 1)
286+ add_search_result (search, 1);
287 });
288
289 var reply_dict = perform_search ("foo", ChannelFlags.DIFF_CHANGES);
290@@ -1089,7 +1106,9 @@
291 int search_state = 1;
292 simple.set_search_func ((search) =>
293 {
294- search_func (search, search_state);
295+ add_search_result (search, 0);
296+ if (search_state == 1)
297+ add_search_result (search, 1);
298 });
299
300 var reply_dict = perform_search ("foo");

Subscribers

People subscribed via source and target branches