Merge lp:~jamesh/libunity/export-multiple-scopes into lp:libunity
- export-multiple-scopes
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michal Hruby |
Approved revision: | 230 |
Merged at revision: | 231 |
Proposed branch: | lp:~jamesh/libunity/export-multiple-scopes |
Merge into: | lp:libunity |
Diff against target: |
371 lines (+182/-61) 4 files modified
debian/libunity9.symbols (+9/-0) src/unity-scope-dbus-connector.vala (+24/-49) src/unity-scope-dbus-impl.vala (+93/-0) test/vala/test-scope.vala (+56/-12) |
To merge this branch: | bzr merge lp:~jamesh/libunity/export-multiple-scopes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michal Hruby (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+165963@code.launchpad.net |
Commit message
Make ScopeDBusConnec
Description of the change
It is currently not possible to export two scopes from the same process using the new Unity.AbstractScope API: the export() call on the second ScopeDBusConnector will fail when it checks to see if the D-Bus name is already in use.
This branch changes the logic a bit, so rather than calling NameHasOwner(name) and erroring out if it returns true, I instead call GetNameOwner(name) and error out if the owner does not match our connection's unique name. If it does match our unique name, I export the scope. If the name has no owner, we go on to request the name as before.
The rationale for this behaviour is that if the process already owns the name, then it is likely to hold on to it for as long as the scope is exported.
PS Jenkins bot (ps-jenkins) wrote : | # |
Michal Hruby (mhr3) wrote : | # |
As mentioned on IRC, this is a bit racy, if two scopes are defined to use the same name, in the time between the first acquiring the name and the second one checking whether the name is already owned, any method calls delivered for the second scope will fail.
We need something like a MultiScopeExporter, or otherwise change the export semantics for this to be safe enough.
- 223. By James Henstridge
-
Merge from trunk
- 224. By James Henstridge
-
Defer acquisition of D-Bus names until Unity.ScopeDBus
Connector. run() is
called, so we can make sure all scopes have been exported prior to
acquiring their associated names.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:224
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 225. By James Henstridge
-
Move the private OwnedName struct to the internal namespace, and update
the symbols file.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:225
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 226. By James Henstridge
-
Update symbols.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:226
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
140 + public static bool acquire_names ()
Does this method really need to be public? I don't see why the loader should need to call it itself. Perhaps the run () method should be able to throw an error instead?
Michal Hruby (mhr3) wrote : | # |
33 + public int ref_count;
Looks very low-level, could you turn the struct into a class with hold() and release() methods and unown the name on last release() call? Should make it more contained.
Also, this still looks like all names that need to be owned are known during the startup. That might not be the case, perhaps the loader starts with exporting a single scope and later another one with a different name is exported? What should the loader impl do in that case?
James Henstridge (jamesh) wrote : | # |
I am using the acquire_names() call from the tests at present. It may also be useful from the loader for the load-scope-
The function is also designed so that it can be run multiple times too: if there is a handle for a particular name, then the name won't be requested again. So calling the function twice should be safe.
- 227. By James Henstridge
-
Move the D-Bus name management into an internal helper class, and make
the acquire_names() call internal (can still be used by tests though). - 228. By James Henstridge
-
Tidy up symbol exports.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:228
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
James Henstridge (jamesh) wrote : | # |
The branch has been updated to move the D-Bus name management into a separate ScopeDBusNameMa
The only new exports now are in the Unity.Internal namespace.
Michal Hruby (mhr3) wrote : | # |
158 + public ScopeDBusNameMa
Singleton class, let's make this private.
164 + public static ScopeDBusNameMa
The rest of libunity uses get_default () for singletons, let's keep it consistent.
225 + warning (@"Unable to own DBus name '$dbus_name'");
226 + Idle.add_full (Priority.DEFAULT, acquire_
You can't resume the async method on first failure to own a name, if there are other own_name calls in progress, their callbacks would cause a crash.
- 229. By James Henstridge
-
Rename ScopeDbusNameMa
nager.get( ) to get_default(), and make constructor
private. - 230. By James Henstridge
-
In acquire_names() wait for all names to be acquired or lost before
returning.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:230
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'debian/libunity9.symbols' |
2 | --- debian/libunity9.symbols 2013-06-10 18:06:29 +0000 |
3 | +++ debian/libunity9.symbols 2013-06-18 05:48:29 +0000 |
4 | @@ -355,6 +355,9 @@ |
5 | unity_internal_io_system_data_dirs_length1@Base 7.0.0daily13.05.31ubuntu.unity.next |
6 | unity_internal_merge_strategy_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next |
7 | unity_internal_merge_strategy_merge_result@Base 7.0.0daily13.05.31ubuntu.unity.next |
8 | + unity_internal_owned_name_dup@Base 0replaceme |
9 | + unity_internal_owned_name_free@Base 0replaceme |
10 | + unity_internal_owned_name_get_type@Base 0replaceme |
11 | unity_internal_result_column_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next |
12 | unity_internal_results_synchronizer_add_provider@Base 7.0.0daily13.05.31ubuntu.unity.next |
13 | unity_internal_results_synchronizer_clear@Base 7.0.0daily13.05.31ubuntu.unity.next |
14 | @@ -393,6 +396,12 @@ |
15 | unity_internal_scope_dbus_impl_set_categories_model@Base 7.0.0daily13.05.31ubuntu.unity.next |
16 | unity_internal_scope_dbus_impl_set_filters_model@Base 7.0.0daily13.05.31ubuntu.unity.next |
17 | unity_internal_scope_dbus_impl_unexport@Base 7.0.0daily13.05.31ubuntu.unity.next |
18 | + unity_internal_scope_dbus_name_manager_acquire_names@Base 0replaceme |
19 | + unity_internal_scope_dbus_name_manager_acquire_names_finish@Base 0replaceme |
20 | + unity_internal_scope_dbus_name_manager_get_default@Base 0replaceme |
21 | + unity_internal_scope_dbus_name_manager_get_type@Base 0replaceme |
22 | + unity_internal_scope_dbus_name_manager_own_name@Base 0replaceme |
23 | + unity_internal_scope_dbus_name_manager_unown_name@Base 0replaceme |
24 | unity_internal_utils_async_mutex_construct@Base 7.0.0daily13.05.31ubuntu.unity.next |
25 | unity_internal_utils_async_mutex_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next |
26 | unity_internal_utils_async_mutex_lock@Base 7.0.0daily13.05.31ubuntu.unity.next |
27 | |
28 | === modified file 'src/unity-scope-dbus-connector.vala' |
29 | --- src/unity-scope-dbus-connector.vala 2013-03-28 13:34:16 +0000 |
30 | +++ src/unity-scope-dbus-connector.vala 2013-06-18 05:48:29 +0000 |
31 | @@ -40,7 +40,7 @@ |
32 | |
33 | private Internal.DefaultScopeDBusImpl pimpl; |
34 | private bool exported; |
35 | - private uint bus_name_own_handle; |
36 | + private bool name_owned; |
37 | |
38 | private static int proxy_usage_count = 0; |
39 | |
40 | @@ -51,8 +51,9 @@ |
41 | if (pimpl == null) pimpl = new Internal.DefaultScopeDBusImpl (scope); |
42 | pimpl.timeout = DAEMON_TIMEOUT_SEC; |
43 | pimpl.on_timeout_reached.connect (timeout_reached); |
44 | - own_name (); // this will also export the object |
45 | + pimpl.export (); |
46 | exported = true; |
47 | + own_name (); |
48 | |
49 | proxy_usage_count++; |
50 | } |
51 | @@ -81,59 +82,22 @@ |
52 | }); |
53 | } |
54 | |
55 | - private void own_name () throws Error |
56 | + private void own_name () |
57 | { |
58 | + var manager = Internal.ScopeDBusNameManager.get_default (); |
59 | var dbus_name = scope.get_group_name (); |
60 | - // check if the name is owned already |
61 | - // FIXME: but track which names does this process own, cause trying to own |
62 | - // an already owned name will fail (even though the caller is the owner)! |
63 | - bool has_owner; |
64 | - DBusConnection bus = Bus.get_sync (BusType.SESSION); |
65 | - Variant result = bus.call_sync ("org.freedesktop.DBus", |
66 | - "/org/freedesktop/dbus", |
67 | - "org.freedesktop.DBus", |
68 | - "NameHasOwner", |
69 | - new Variant ("(s)", dbus_name), |
70 | - new VariantType ("(b)"), |
71 | - DBusCallFlags.NO_AUTO_START, |
72 | - -1); |
73 | - result.get ("(b)", out has_owner); |
74 | - if (has_owner) |
75 | - { |
76 | - throw new IOError.FAILED ("Name '%s' is already owned".printf (dbus_name)); |
77 | - } |
78 | - |
79 | - bool got_name = false; |
80 | - var ml = new MainLoop (); |
81 | - bus_name_own_handle = Bus.own_name ( |
82 | - BusType.SESSION, dbus_name, BusNameOwnerFlags.NONE, () => |
83 | - { |
84 | - // connection acquired callback |
85 | - try |
86 | - { |
87 | - pimpl.export (); |
88 | - } |
89 | - catch (Error err) |
90 | - { |
91 | - warning ("Unable to export object: %s", err.message); |
92 | - } |
93 | - }, |
94 | - () => { got_name = true; ml.quit (); }, |
95 | - () => { ml.quit (); } |
96 | - ); |
97 | - ml.run (); |
98 | - if (!got_name) |
99 | - { |
100 | - throw new IOError.FAILED ("Unable to own DBus name '%s'".printf (dbus_name)); |
101 | - } |
102 | + manager.own_name (dbus_name); |
103 | + name_owned = true; |
104 | } |
105 | |
106 | - private void unown_name () |
107 | + private void unown_name() |
108 | { |
109 | - if (bus_name_own_handle != 0) |
110 | + if (name_owned) |
111 | { |
112 | - Bus.unown_name (bus_name_own_handle); |
113 | - bus_name_own_handle = 0; |
114 | + var manager = Internal.ScopeDBusNameManager.get_default (); |
115 | + var dbus_name = scope.get_group_name (); |
116 | + manager.unown_name (dbus_name); |
117 | + name_owned = false; |
118 | } |
119 | } |
120 | |
121 | @@ -142,6 +106,17 @@ |
122 | public static void run () |
123 | { |
124 | if (primary_loop == null) primary_loop = new MainLoop (); |
125 | + |
126 | + var manager = Internal.ScopeDBusNameManager.get_default (); |
127 | + manager.acquire_names.begin ((obj, result) => |
128 | + { |
129 | + if (!manager.acquire_names.end (result)) |
130 | + { |
131 | + warning ("Failed to acquire all required D-Bus names"); |
132 | + primary_loop.quit (); |
133 | + } |
134 | + }); |
135 | + |
136 | primary_loop.run (); |
137 | } |
138 | |
139 | |
140 | === modified file 'src/unity-scope-dbus-impl.vala' |
141 | --- src/unity-scope-dbus-impl.vala 2013-06-10 17:56:20 +0000 |
142 | +++ src/unity-scope-dbus-impl.vala 2013-06-18 05:48:29 +0000 |
143 | @@ -786,4 +786,97 @@ |
144 | } |
145 | } |
146 | |
147 | +private struct OwnedName { |
148 | + public int ref_count; |
149 | + public uint bus_name_own_handle; |
150 | +} |
151 | + |
152 | +private class ScopeDBusNameManager : Object |
153 | +{ |
154 | + private HashTable<string,OwnedName?> owned_names; |
155 | + |
156 | + private ScopeDBusNameManager () |
157 | + { |
158 | + owned_names = new HashTable<string,OwnedName?> (str_hash, str_equal); |
159 | + } |
160 | + |
161 | + static ScopeDBusNameManager? name_manager; |
162 | + public static ScopeDBusNameManager get_default () |
163 | + { |
164 | + if (name_manager == null) |
165 | + { |
166 | + name_manager = new ScopeDBusNameManager (); |
167 | + } |
168 | + return name_manager; |
169 | + } |
170 | + |
171 | + public void own_name (string dbus_name) |
172 | + { |
173 | + unowned OwnedName? info = owned_names.get (dbus_name); |
174 | + if (info != null) |
175 | + { |
176 | + info.ref_count += 1; |
177 | + } |
178 | + else |
179 | + { |
180 | + owned_names.insert (dbus_name, {1, 0}); |
181 | + } |
182 | + } |
183 | + |
184 | + public void unown_name (string dbus_name) |
185 | + { |
186 | + unowned Internal.OwnedName? info = owned_names.get (dbus_name); |
187 | + if (info != null) |
188 | + { |
189 | + info.ref_count -= 1; |
190 | + // Ref count dropped to zero => release the name if it has been acquired |
191 | + if (info.ref_count <= 0) |
192 | + { |
193 | + if (info.bus_name_own_handle != 0) |
194 | + { |
195 | + Bus.unown_name (info.bus_name_own_handle); |
196 | + } |
197 | + owned_names.remove(dbus_name); |
198 | + } |
199 | + } |
200 | + } |
201 | + |
202 | + public async bool acquire_names () |
203 | + { |
204 | + var count = 0; |
205 | + var failures = 0; |
206 | + owned_names.for_each ((dbus_name, info) => |
207 | + { |
208 | + if (info.bus_name_own_handle != 0) |
209 | + { |
210 | + // The name has already been requested. |
211 | + return; |
212 | + } |
213 | + count += 1; |
214 | + info.bus_name_own_handle = Bus.own_name ( |
215 | + BusType.SESSION, dbus_name, BusNameOwnerFlags.NONE, null, |
216 | + () => { |
217 | + count -= 1; |
218 | + if (count <= 0) |
219 | + { |
220 | + Idle.add_full (Priority.DEFAULT, acquire_names.callback); |
221 | + } |
222 | + }, |
223 | + () => { |
224 | + failures += 1; |
225 | + warning (@"Unable to own DBus name '$dbus_name'"); |
226 | + count -= 1; |
227 | + if (count <= 0) |
228 | + { |
229 | + Idle.add_full (Priority.DEFAULT, acquire_names.callback); |
230 | + } |
231 | + } |
232 | + ); |
233 | + }); |
234 | + yield; |
235 | + return failures == 0; |
236 | + } |
237 | + |
238 | +} |
239 | + |
240 | } /* namespace */ |
241 | |
242 | === modified file 'test/vala/test-scope.vala' |
243 | --- test/vala/test-scope.vala 2013-05-30 23:42:30 +0000 |
244 | +++ test/vala/test-scope.vala 2013-06-18 05:48:29 +0000 |
245 | @@ -86,6 +86,8 @@ |
246 | |
247 | Test.add_data_func ("/Unit/Scope/Export", |
248 | Fixture.create<ScopeInitTester> (ScopeInitTester.test_scope_export)); |
249 | + Test.add_data_func ("/Unit/Scope/ExportTwoScopes", |
250 | + Fixture.create<ScopeInitTester> (ScopeInitTester.test_scope_export_two_scopes)); |
251 | Test.add_data_func ("/Unit/Scope/ProxyConnection", |
252 | Fixture.create<ScopeInitTester> (ScopeInitTester.test_scope_proxy)); |
253 | Test.add_data_func ("/Unit/Scope/OpenChannel", |
254 | @@ -204,11 +206,12 @@ |
255 | |
256 | class TestScope: Unity.AbstractScope |
257 | { |
258 | + public string dbus_name { get; construct set; } |
259 | public string dbus_path { get; construct set; } |
260 | |
261 | - public TestScope (string dbus_path) |
262 | + public TestScope (string dbus_name, string dbus_path) |
263 | { |
264 | - Object (dbus_path: dbus_path); |
265 | + Object (dbus_name: dbus_name, dbus_path: dbus_path); |
266 | } |
267 | |
268 | public override Unity.ScopeSearchBase create_search_for_query (Unity.SearchContext ctx) |
269 | @@ -269,7 +272,7 @@ |
270 | |
271 | public override string get_group_name () |
272 | { |
273 | - return DBUS_NAME; |
274 | + return dbus_name; |
275 | } |
276 | |
277 | public override string get_unique_name () |
278 | @@ -317,6 +320,20 @@ |
279 | return proxy; |
280 | } |
281 | |
282 | + public static bool acquire_names () |
283 | + { |
284 | + var ml = new MainLoop (); |
285 | + var manager = Unity.Internal.ScopeDBusNameManager.get_default (); |
286 | + bool success = false; |
287 | + manager.acquire_names.begin ((obj, result) => |
288 | + { |
289 | + success = manager.acquire_names.end (result); |
290 | + ml.quit(); |
291 | + }); |
292 | + ml.run (); |
293 | + return success; |
294 | + } |
295 | + |
296 | private void setup () |
297 | { |
298 | } |
299 | @@ -328,22 +345,48 @@ |
300 | |
301 | public void test_scope_export () |
302 | { |
303 | - scope = new TestScope (DBUS_PATH); |
304 | - try |
305 | - { |
306 | - scope.export (); |
307 | - } |
308 | - catch (Error err) { assert_not_reached (); } |
309 | + scope = new TestScope (DBUS_NAME, DBUS_PATH); |
310 | + try |
311 | + { |
312 | + scope.export (); |
313 | + } |
314 | + catch (Error err) { assert_not_reached (); } |
315 | + |
316 | + assert (acquire_names ()); |
317 | + } |
318 | + |
319 | + public void test_scope_export_two_scopes () |
320 | + { |
321 | + scope = new TestScope (DBUS_NAME, DBUS_PATH); |
322 | + try |
323 | + { |
324 | + scope.export (); |
325 | + } |
326 | + catch (Error err) { assert_not_reached (); } |
327 | + |
328 | + // Export a second scope using the same D-Bus name |
329 | + var scope2 = new TestScope (DBUS_NAME, "/second/scope/path"); |
330 | + try |
331 | + { |
332 | + scope2.export (); |
333 | + } |
334 | + catch (Error err) { assert_not_reached (); } |
335 | + finally |
336 | + { |
337 | + scope2.unexport (); |
338 | + } |
339 | + assert (acquire_names ()); |
340 | } |
341 | |
342 | public void test_scope_proxy () |
343 | { |
344 | - scope = new TestScope (DBUS_PATH); |
345 | + scope = new TestScope (DBUS_NAME, DBUS_PATH); |
346 | try |
347 | { |
348 | scope.export (); |
349 | } |
350 | catch (Error err) { assert_not_reached (); } |
351 | + assert (acquire_names ()); |
352 | |
353 | var proxy = acquire_test_proxy (); |
354 | assert (proxy != null); |
355 | @@ -363,13 +406,14 @@ |
356 | { |
357 | base.setup (); |
358 | |
359 | - scope = new TestScope (DBUS_PATH); |
360 | - |
361 | + scope = new TestScope (DBUS_NAME, DBUS_PATH); |
362 | + |
363 | try |
364 | { |
365 | scope.export (); |
366 | } |
367 | catch (Error err) { assert_not_reached (); } |
368 | + assert (acquire_names ()); |
369 | |
370 | proxy = ScopeInitTester.acquire_test_proxy (); |
371 | assert (proxy != null); |
PASSED: Continuous integration, rev:222 jenkins. qa.ubuntu. com/job/ libunity- ci/12/ jenkins. qa.ubuntu. com/job/ libunity- raring- amd64-ci/ 12 jenkins. qa.ubuntu. com/job/ libunity- raring- armhf-ci/ 12 jenkins. qa.ubuntu. com/job/ libunity- raring- i386-ci/ 12
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ libunity- ci/12/rebuild
http://