Merge lp:~marcustomlinson/unity-scopes-api/fix_ui_policy into lp:unity-scopes-api

Proposed by Marcus Tomlinson
Status: Superseded
Proposed branch: lp:~marcustomlinson/unity-scopes-api/fix_ui_policy
Merge into: lp:unity-scopes-api
Diff against target: 867 lines (+426/-137)
9 files modified
CMakeLists.txt (+1/-1)
HACKING (+22/-1)
RELEASE_NOTES.md (+5/-0)
debian/changelog (+7/-0)
include/unity/scopes/OnlineAccountClient.h (+8/-9)
include/unity/scopes/internal/OnlineAccountClientImpl.h (+23/-13)
src/scopes/internal/OnlineAccountClientImpl.cpp (+347/-112)
src/scopes/internal/RegistryObject.cpp (+11/-1)
test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp (+2/-0)
To merge this branch: bzr merge lp:~marcustomlinson/unity-scopes-api/fix_ui_policy
Reviewer Review Type Date Requested Status
Paweł Stołowski (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+237216@code.launchpad.net

This proposal has been superseded by a proposal from 2014-10-08.

Commit message

1. Depending on whether OnlineAccountClient is used by the shell or by a scope, set the SIGNON_SESSION_DATA_UI_POLICY accordingly.

2. Inform scopes of successful user authentications via a pub/sub network.

3. Removed inc_logins() and dec_logins() as only one session can be active per service (now using info->session != nullptr).

4. Added better thread and exception safety.

5. Run the internal event loop within its own context to avoid clashing with external main loops.

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

+1

review: Approve
264. By Marcus Tomlinson

Merged devel

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-09-11 08:35:01 +0000
3+++ CMakeLists.txt 2014-10-08 10:59:41 +0000
4@@ -195,7 +195,7 @@
5 # API version
6 set(UNITY_SCOPES_MAJOR 0)
7 set(UNITY_SCOPES_MINOR 6)
8-set(UNITY_SCOPES_MICRO 6)
9+set(UNITY_SCOPES_MICRO 7)
10 set(UNITY_SCOPES_SOVERSION 3)
11
12 # Version for testing, with all symbols visible
13
14=== modified file 'HACKING'
15--- HACKING 2014-09-08 22:15:40 +0000
16+++ HACKING 2014-10-08 10:59:41 +0000
17@@ -156,7 +156,28 @@
18 This creates a diff of the symbols in /tmp/symbols.diff.
19 (The demangled symbols from the debian build are in ./new_symbols.)
20
21-Review any changes in /tmp/symbols.diff. If they are OK:
22+NOTE: We currently have one architecture-specific symbol:
23+
24+(c++|arch=amd64)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0.6.6+14.10.20140916
25+
26+We need this because the copy-constructor is inlined on i386 and armhf.
27+
28+The above script can't cope with the arch tag at the moment. Until we get
29+around to fixing this, you need to delete the corresponding change from
30+the diff output before applying the diff as a patch. The offending diff
31+will look something like this:
32+
33+@@ -377,7 +378,7 @@
34+ (c++)"unity::scopes::internal::RegistryObject::remove_desktop_file(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.6.6+14.10.20140916
35+ (c++)"unity::scopes::internal::RegistryObject::remove_local_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
36+ (c++)"unity::scopes::internal::RegistryObject::ScopeExecData::~ScopeExecData()@Base" 0.4.2+14.04.20140404.2
37+- (c++|arch=amd64)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0.6.6+14.10.20140916
38++ (c++)"unity::scopes::internal::RegistryObject::ScopeExecData::ScopeExecData(unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0replaceme
39+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::clear_handle_unlocked()@Base" 0.4.2+14.04.20140404.2
40+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::exec(core::posix::ChildProcess::DeathObserver&, std::shared_ptr<unity::scopes::internal::Executor>)@Base" 0.4.3+14.10.20140428
41+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::expand_custom_exec()@Base" 0.5.0+14.10.20140619
42+
43+Review any other changes in /tmp/symbols.diff. If they are OK:
44
45 $ cd -
46 $ patch -p0 < /tmp/symbols.diff
47
48=== modified file 'RELEASE_NOTES.md'
49--- RELEASE_NOTES.md 2014-09-11 08:42:38 +0000
50+++ RELEASE_NOTES.md 2014-10-08 10:59:41 +0000
51@@ -1,6 +1,11 @@
52 Release notes
53 =============
54
55+Changes in version 0.6.7
56+========================
57+ - Added new RunInExternalUiMainLoop option to OnlineAccountClient::MainLoopSelect.
58+ - OnlineAccountClient signon UI policy determined by main loop used (shell: show / scope: hide).
59+
60 Changes in version 0.6.6
61 ========================
62 - Added support for online accounts (via new OnlineAccountClient class).
63
64=== modified file 'debian/changelog'
65--- debian/changelog 2014-10-06 12:03:20 +0000
66+++ debian/changelog 2014-10-08 10:59:41 +0000
67@@ -1,3 +1,10 @@
68+unity-scopes-api (0.6.7-0ubuntu1) UNRELEASED; urgency=medium
69+
70+ * Added new RunInExternalUiMainLoop option to OnlineAccountClient::MainLoopSelect.
71+ * OnlineAccountClient signon UI policy determined by main loop used (shell: show / scope: hide).
72+
73+ -- Marcus Tomlinson <marcus.tomlinson@canonical.com> Wed, 01 Oct 2014 15:46:30 +0200
74+
75 unity-scopes-api (0.6.6+14.10.20141006-0ubuntu1) utopic; urgency=medium
76
77 [ Pawel Stolowski ]
78
79=== modified file 'include/unity/scopes/OnlineAccountClient.h'
80--- include/unity/scopes/OnlineAccountClient.h 2014-09-15 05:10:17 +0000
81+++ include/unity/scopes/OnlineAccountClient.h 2014-10-08 10:59:41 +0000
82@@ -55,7 +55,7 @@
83 */
84 struct ServiceStatus
85 {
86- uint account_id; ///< A unique ID of the online account parenting this service.
87+ int account_id; ///< A unique ID of the online account parenting this service.
88 bool service_enabled; ///< True if this service is enabled.
89 bool service_authenticated; ///< True if this service is authenticated.
90 std::string client_id; ///< "ConsumerKey" / "ClientId" OAuth (1 / 2) parameter.
91@@ -68,16 +68,15 @@
92 /**
93 \brief Indicates whether an external main loop already exists, or one should be created internally.
94
95- A running main loop is essential in order to receive service update callbacks. When in doubt, set
96- RunInExternalMainLoop and just use refresh_service_statuses() and get_service_statuses() to obtain
97- service statuses.
98+ A running main loop is essential in order to receive service updates from the online accounts
99+ backend. When in doubt, set to CreateInternalMainLoop.
100 */
101 enum MainLoopSelect
102 {
103- RunInExternalMainLoop, ///< An external main loop already exists or the service update
104- /// callback is not required.
105- CreateInternalMainLoop ///< An external main loop does not exist and the service update
106- /// callback is required.
107+ RunInExternalMainLoop, ///< An external main loop already exists and is running.
108+ CreateInternalMainLoop, ///< An external main loop does not exist.
109+ RunInExternalUiMainLoop, ///< An external UI main loop exists and is running (Intended for
110+ /// shell use only. A scope should not be running a UI main loop).
111 };
112
113 /**
114@@ -91,7 +90,7 @@
115 OnlineAccountClient(std::string const& service_name,
116 std::string const& service_type,
117 std::string const& provider_name,
118- MainLoopSelect main_loop_select = RunInExternalMainLoop);
119+ MainLoopSelect main_loop_select = CreateInternalMainLoop);
120
121 /// @cond
122 ~OnlineAccountClient();
123
124=== modified file 'include/unity/scopes/internal/OnlineAccountClientImpl.h'
125--- include/unity/scopes/internal/OnlineAccountClientImpl.h 2014-09-11 11:29:29 +0000
126+++ include/unity/scopes/internal/OnlineAccountClientImpl.h 2014-10-08 10:59:41 +0000
127@@ -19,6 +19,7 @@
128 #ifndef UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H
129 #define UNITY_SCOPES_INTERNAL_ONLINEACCOUNTCLIENTIMPL_H
130
131+#include <unity/scopes/internal/MiddlewareBase.h>
132 #include <unity/scopes/OnlineAccountClient.h>
133
134 #include <libaccounts-glib/accounts-glib.h>
135@@ -78,42 +79,51 @@
136 OnlineAccountClient::PostLoginAction login_failed_action);
137
138 // Methods used only by impl
139- void flush_pending_sessions();
140+ void construct();
141+ void tear_down();
142+
143+ void flush_pending_session(AgAccountId const& account_id, std::unique_lock<std::mutex>& lock);
144
145 void main_loop_state_notify(bool is_running);
146
147+ std::shared_ptr<AgManager> manager();
148 std::string service_name();
149- void callback(OnlineAccountClient::ServiceStatus const& service_status);
150+ OnlineAccountClient::MainLoopSelect main_loop_select();
151+
152+ void callback(AccountInfo const* info, std::string const& error = "");
153
154 bool has_account(AgAccountId const& account_id);
155 void add_account(AgAccountId const& account_id, std::shared_ptr<AccountInfo> account_info);
156 void remove_account(AgAccountId const& account_id);
157
158- bool inc_logins();
159- void dec_logins();
160-
161 private:
162- std::string service_name_;
163- std::string service_type_;
164- std::string provider_name_;
165+ std::string const service_name_;
166+ std::string const service_type_;
167+ std::string const provider_name_;
168+ OnlineAccountClient::MainLoopSelect const main_loop_select_;
169+
170+ std::mutex callback_mutex_;
171 OnlineAccountClient::ServiceUpdateCallback callback_;
172- bool use_external_main_loop_;
173
174- std::thread main_loop_thread_;
175- std::mutex callback_mutex_;
176 std::mutex mutex_;
177 std::condition_variable cond_;
178+ std::thread main_loop_thread_;
179 bool main_loop_is_running_;
180- bool client_stopping_;
181- int logins_busy_;
182+ std::exception_ptr thread_exception_;
183 gulong account_enabled_signal_id_;
184 gulong account_deleted_signal_id_;
185
186 std::shared_ptr<GMainLoop> main_loop_;
187+ std::shared_ptr<GMainContext> main_loop_context_;
188 std::shared_ptr<AgManager> manager_;
189 std::map<AgAccountId, std::shared_ptr<AccountInfo>> accounts_;
190
191+ MiddlewareBase::SPtr mw_;
192+ MWPublisher::UPtr auth_publisher_;
193+ MWSubscriber::UPtr auth_subscriber_;
194+
195 void main_loop_thread();
196+ void auth_callback(std::string const& details_json);
197 };
198
199 } // namespace internal
200
201=== modified file 'src/scopes/internal/OnlineAccountClientImpl.cpp'
202--- src/scopes/internal/OnlineAccountClientImpl.cpp 2014-09-15 07:06:51 +0000
203+++ src/scopes/internal/OnlineAccountClientImpl.cpp 2014-10-08 10:59:41 +0000
204@@ -17,7 +17,12 @@
205 */
206
207 #include <unity/scopes/internal/OnlineAccountClientImpl.h>
208-#include <unity/util/ResourcePtr.h>
209+
210+#include <unity/scopes/internal/JsonCppNode.h>
211+#include <unity/scopes/internal/MiddlewareFactory.h>
212+#include <unity/scopes/internal/RuntimeConfig.h>
213+#include <unity/scopes/internal/RuntimeImpl.h>
214+#include <unity/UnityExceptions.h>
215
216 #include <iostream>
217
218@@ -88,7 +93,41 @@
219 return service_status;
220 }
221
222-static void clear_session_info(AccountInfo* info)
223+static std::string details_to_json(OnlineAccountClient::ServiceStatus const& details)
224+{
225+ VariantMap vm;
226+ vm["account_id"] = details.account_id;
227+ vm["service_enabled"] = details.service_enabled;
228+ vm["service_authenticated"] = details.service_authenticated;
229+ vm["client_id"] = details.client_id;
230+ vm["client_secret"] = details.client_secret;
231+ vm["access_token"] = details.access_token;
232+ vm["token_secret"] = details.token_secret;
233+ vm["error"] = details.error;
234+
235+ Variant var(vm);
236+ JsonCppNode node(var);
237+ return node.to_json_string();
238+}
239+
240+static OnlineAccountClient::ServiceStatus json_to_details(std::string const& json)
241+{
242+ OnlineAccountClient::ServiceStatus details;
243+ JsonCppNode node(json);
244+
245+ details.account_id = node.get_node("account_id")->as_int();
246+ details.service_enabled = node.get_node("service_enabled")->as_bool();
247+ details.service_authenticated = node.get_node("service_authenticated")->as_bool();
248+ details.client_id = node.get_node("client_id")->as_string();
249+ details.client_secret = node.get_node("client_secret")->as_string();
250+ details.access_token = node.get_node("access_token")->as_string();
251+ details.token_secret = node.get_node("token_secret")->as_string();
252+ details.error = node.get_node("error")->as_string();
253+
254+ return details;
255+}
256+
257+static void clear_session(AccountInfo* info)
258 {
259 if (info->session)
260 {
261@@ -96,8 +135,6 @@
262 signon_auth_session_cancel(info->session.get());
263 }
264 info->session = nullptr;
265- info->auth_params = nullptr;
266- info->session_data = nullptr;
267 }
268
269 static gboolean main_loop_is_running_cb(void* user_data)
270@@ -107,6 +144,13 @@
271 return G_SOURCE_REMOVE;
272 }
273
274+static gboolean main_loop_is_stopping_cb(void* user_data)
275+{
276+ OnlineAccountClientImpl* account_client = reinterpret_cast<OnlineAccountClientImpl*>(user_data);
277+ account_client->main_loop_state_notify(false);
278+ return G_SOURCE_REMOVE;
279+}
280+
281 static gboolean wake_up_event_loop_cb(void* user_data)
282 {
283 GMainLoop* event_loop = reinterpret_cast<GMainLoop*>(user_data);
284@@ -116,58 +160,75 @@
285
286 static void service_login_cb(GObject* source, GAsyncResult* result, void* user_data)
287 {
288- GError* error = nullptr;
289- util::ResourcePtr<GError*, decltype(&g_error_free)> error_cleanup(error, free_error);
290-
291 SignonAuthSession* session = reinterpret_cast<SignonAuthSession*>(source);
292 AccountInfo* info = reinterpret_cast<AccountInfo*>(user_data);
293
294 // Get session data then send a notification with the login result
295+ GError* error = nullptr;
296 info->session_data.reset(signon_auth_session_process_finish(session, result, &error), free_variant);
297- info->account_client->callback(info_to_details(info, error ? error->message : ""));
298- info->account_client->dec_logins();
299+ std::shared_ptr<GError> error_cleanup(error, free_error);
300+
301+ info->account_client->callback(info, error ? error->message : "");
302+
303+ // Clear session info
304+ clear_session(info);
305 }
306
307 static void service_update_cb(AgAccountService* account_service, gboolean enabled, AccountInfo* info)
308 {
309- GError* error = nullptr;
310- util::ResourcePtr<GError*, decltype(&g_error_free)> error_cleanup(error, free_error);
311-
312 // Service state has updated, clear the old session data
313 info->service_enabled = enabled;
314- clear_session_info(info);
315+ clear_session(info);
316
317 if (enabled)
318 {
319 // Get authorization data then create a new authorization session
320 std::shared_ptr<AgAuthData> auth_data(ag_account_service_get_auth_data(account_service), ag_auth_data_unref);
321+
322+ GError* error = nullptr;
323 info->session.reset(signon_auth_session_new(
324 ag_auth_data_get_credentials_id(auth_data.get()), ag_auth_data_get_method(auth_data.get()), &error), g_object_unref);
325+ std::shared_ptr<GError> error_cleanup(error, free_error);
326+
327 if (error)
328 {
329 // Send notification that the authorization session failed
330- info->account_client->callback(info_to_details(info, error->message));
331+ info->account_client->callback(info, error->message);
332 return;
333 }
334
335 // Get authorization parameters then attempt to signon
336+ GVariantBuilder builder;
337+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
338+
339+ if (info->account_client->main_loop_select() == OnlineAccountClient::RunInExternalUiMainLoop)
340+ {
341+ g_variant_builder_add(&builder, "{sv}",
342+ SIGNON_SESSION_DATA_UI_POLICY,
343+ g_variant_new_int32(SIGNON_POLICY_DEFAULT));
344+ }
345+ else
346+ {
347+ g_variant_builder_add(&builder, "{sv}",
348+ SIGNON_SESSION_DATA_UI_POLICY,
349+ g_variant_new_int32(SIGNON_POLICY_NO_USER_INTERACTION));
350+ }
351+
352 info->auth_params.reset(
353- g_variant_ref_sink(ag_auth_data_get_login_parameters(auth_data.get(), nullptr)), free_variant);
354+ g_variant_ref_sink(ag_auth_data_get_login_parameters(auth_data.get(), g_variant_builder_end(&builder))), free_variant);
355
356- if (info->account_client->inc_logins())
357- {
358- signon_auth_session_process_async(info->session.get(),
359- info->auth_params.get(),
360- ag_auth_data_get_mechanism(auth_data.get()),
361- nullptr,
362- service_login_cb,
363- info);
364- }
365+ // Start signon process
366+ signon_auth_session_process_async(info->session.get(),
367+ info->auth_params.get(),
368+ ag_auth_data_get_mechanism(auth_data.get()),
369+ nullptr,
370+ service_login_cb,
371+ info);
372 }
373 else
374 {
375 // Send notification that account has been disabled
376- info->account_client->callback(info_to_details(info));
377+ info->account_client->callback(info);
378 }
379 }
380
381@@ -193,7 +254,7 @@
382 if (account_client->service_name() == ag_service_get_name(service))
383 {
384 std::shared_ptr<AgAccountService> account_service(ag_account_service_new(account.get(), service), g_object_unref);
385- std::shared_ptr<AccountInfo> info(new AccountInfo, [](AccountInfo* info){ info->account_client = nullptr; delete info; });
386+ std::shared_ptr<AccountInfo> info(new AccountInfo);
387 info->service_enabled = false;
388 info->account_client = account_client;
389 info->account_service.reset(reinterpret_cast<AgAccountService*>(g_object_ref(account_service.get())), g_object_unref);
390@@ -226,51 +287,95 @@
391 : service_name_(service_name)
392 , service_type_(service_type)
393 , provider_name_(provider_name)
394- , use_external_main_loop_(main_loop_select == OnlineAccountClient::RunInExternalMainLoop)
395- , main_loop_is_running_(false)
396- , client_stopping_(false)
397- , logins_busy_(0)
398+ , main_loop_select_(main_loop_select)
399+ , main_loop_is_running_(main_loop_select != OnlineAccountClient::CreateInternalMainLoop)
400 {
401+ // Set up authentication pub/sub
402+ std::string oa_id = provider_name + ":" + service_type + ":" + service_name;
403+ RuntimeImpl::UPtr rt = RuntimeImpl::create(oa_id);
404+ MiddlewareFactory mw_factory(rt.get());
405+ RuntimeConfig rt_config("");
406+
407+ mw_ = mw_factory.create(oa_id, rt_config.default_middleware(), rt_config.default_middleware_configfile());
408+ if (main_loop_select == OnlineAccountClient::RunInExternalUiMainLoop)
409+ {
410+ // If this client was created by the shell, set up as the publisher
411+ auth_publisher_ = mw_->create_publisher(oa_id);
412+ }
413+ else
414+ {
415+ // If this client was created by a scope, set up as a subscriber
416+ auth_subscriber_ = mw_->create_subscriber(oa_id);
417+ auth_subscriber_->message_received().connect(std::bind(&OnlineAccountClientImpl::auth_callback, this, std::placeholders::_1));
418+ }
419+
420 // If we are responsible for the main loop
421- if (!use_external_main_loop_)
422+ if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
423 {
424 // Wait here until either the main loop begins running, or the thread exits
425- std::unique_lock<std::mutex> lock(mutex_);
426- main_loop_thread_ = std::thread(&OnlineAccountClientImpl::main_loop_thread, this);
427- cond_.wait(lock, [this] { return main_loop_is_running_; });
428- }
429-
430- manager_.reset(ag_manager_new_for_service_type(service_type_.c_str()), g_object_unref);
431-
432- // Watch for changes to accounts
433- account_enabled_signal_id_ = g_signal_connect(manager_.get(), "enabled-event", G_CALLBACK(account_enabled_cb), this);
434- account_deleted_signal_id_ = g_signal_connect(manager_.get(), "account-deleted", G_CALLBACK(account_deleted_cb), this);
435-
436- // Now check initial state
437- refresh_service_statuses();
438+ {
439+ std::unique_lock<std::mutex> lock(mutex_);
440+ main_loop_thread_ = std::thread(&OnlineAccountClientImpl::main_loop_thread, this);
441+ cond_.wait_for(lock, std::chrono::seconds(5), [this] { return main_loop_is_running_; });
442+ }
443+ if (!main_loop_is_running_)
444+ {
445+ if (main_loop_)
446+ {
447+ // Quit the main loop, causing the thread to exit
448+ g_main_loop_quit(main_loop_.get());
449+ if (main_loop_thread_.joinable())
450+ {
451+ main_loop_thread_.join();
452+ }
453+ }
454+ if (thread_exception_)
455+ {
456+ std::rethrow_exception(thread_exception_);
457+ }
458+ else
459+ {
460+ throw unity::ResourceException("OnlineAccountClientImpl(): main_loop_thread failed to start.");
461+ }
462+ }
463+ }
464+ else
465+ {
466+ construct();
467+ }
468 }
469
470 OnlineAccountClientImpl::~OnlineAccountClientImpl()
471 {
472- // Disconnect all signal handlers
473- g_signal_handler_disconnect(manager_.get(), account_enabled_signal_id_);
474- g_signal_handler_disconnect(manager_.get(), account_deleted_signal_id_);
475-
476- for (auto info : accounts_)
477- {
478- g_signal_handler_disconnect(info.second->account_service.get(), info.second->service_update_signal_id_);
479- }
480-
481- // Wait until all currently running login sessions are done
482- {
483- std::unique_lock<std::mutex> lock(mutex_);
484- client_stopping_ = true;
485- }
486- flush_pending_sessions();
487-
488- // If we are responsible for the main loop, quit it on destruction
489- if (main_loop_)
490- {
491+ {
492+ std::lock_guard<std::mutex> lock(mutex_);
493+ if (thread_exception_)
494+ {
495+ try
496+ {
497+ std::rethrow_exception(thread_exception_);
498+ }
499+ catch (std::exception const& e)
500+ {
501+ std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an exception: " << e.what() << std::endl;
502+ }
503+ catch (...)
504+ {
505+ std::cerr << "~OnlineAccountClientImpl(): main_loop_thread threw an unknown exception" << std::endl;
506+ }
507+ }
508+ }
509+
510+ // If we are responsible for the main loop
511+ if (main_loop_select_ == OnlineAccountClient::CreateInternalMainLoop)
512+ {
513+ // Invoke tear_down() from within the main loop
514+ {
515+ std::unique_lock<std::mutex> lock(mutex_);
516+ g_main_context_invoke(main_loop_context_.get(), main_loop_is_stopping_cb, this);
517+ cond_.wait(lock, [this] { return !main_loop_is_running_; });
518+ }
519+
520 // Quit the main loop, causing the thread to exit
521 g_main_loop_quit(main_loop_.get());
522 if (main_loop_thread_.joinable())
523@@ -278,16 +383,32 @@
524 main_loop_thread_.join();
525 }
526 }
527+ else
528+ {
529+ tear_down();
530+ }
531 }
532
533 void OnlineAccountClientImpl::set_service_update_callback(OnlineAccountClient::ServiceUpdateCallback callback)
534 {
535- std::lock_guard<std::mutex> lock(callback_mutex_);
536+ std::lock_guard<std::mutex> lock(mutex_);
537+ if (thread_exception_)
538+ {
539+ std::rethrow_exception(thread_exception_);
540+ }
541+
542+ std::lock_guard<std::mutex> callback_lock(callback_mutex_);
543 callback_ = callback;
544 }
545
546 void OnlineAccountClientImpl::refresh_service_statuses()
547 {
548+ std::unique_lock<std::mutex> lock(mutex_);
549+ if (thread_exception_)
550+ {
551+ std::rethrow_exception(thread_exception_);
552+ }
553+
554 std::shared_ptr<GList> enabled_accounts(ag_manager_list(manager_.get()), ag_manager_list_free);
555 GList* it;
556 for (it = enabled_accounts.get(); it; it = it->next)
557@@ -297,19 +418,25 @@
558 std::string provider_name = ag_account_get_provider_name(account.get());
559 if (provider_name == provider_name_)
560 {
561+ lock.unlock();
562 account_enabled_cb(manager_.get(), account_id, this);
563+ lock.lock();
564 }
565+ flush_pending_session(account_id, lock);
566 }
567- flush_pending_sessions();
568 }
569
570 std::vector<OnlineAccountClient::ServiceStatus> OnlineAccountClientImpl::get_service_statuses()
571 {
572 std::lock_guard<std::mutex> lock(mutex_);
573+ if (thread_exception_)
574+ {
575+ std::rethrow_exception(thread_exception_);
576+ }
577
578 // Return all service statuses
579 std::vector<OnlineAccountClient::ServiceStatus> service_statuses;
580- for (auto const info : accounts_)
581+ for (auto const& info : accounts_)
582 {
583 service_statuses.push_back(info_to_details(info.second.get()));
584 }
585@@ -352,42 +479,112 @@
586 widget.add_attribute_value("online_account_details", Variant(account_details_map));
587 }
588
589-void OnlineAccountClientImpl::flush_pending_sessions()
590-{
591+void OnlineAccountClientImpl::construct()
592+{
593+ manager_.reset(ag_manager_new_for_service_type(service_type_.c_str()), g_object_unref);
594+
595+ // Watch for changes to accounts
596+ account_enabled_signal_id_ = g_signal_connect(manager_.get(), "enabled-event", G_CALLBACK(account_enabled_cb), this);
597+ account_deleted_signal_id_ = g_signal_connect(manager_.get(), "account-deleted", G_CALLBACK(account_deleted_cb), this);
598+
599+ // Now check initial state
600+ refresh_service_statuses();
601+}
602+
603+void OnlineAccountClientImpl::tear_down()
604+{
605+ // Disconnect signal handlers
606+ g_signal_handler_disconnect(manager_.get(), account_enabled_signal_id_);
607+ g_signal_handler_disconnect(manager_.get(), account_deleted_signal_id_);
608+
609+ // Remove all accounts
610+ {
611+ std::unique_lock<std::mutex> lock(mutex_);
612+ for (auto const& info : accounts_)
613+ {
614+ // Before we nuke the map, ensure that any pending sessions are done
615+ g_signal_handler_disconnect(info.second->account_service.get(), info.second->service_update_signal_id_);
616+ clear_session(info.second.get());
617+ flush_pending_session(info.second->account_id, lock);
618+ }
619+ accounts_.clear();
620+ }
621+}
622+
623+void OnlineAccountClientImpl::flush_pending_session(AgAccountId const& account_id, std::unique_lock<std::mutex>& lock)
624+{
625+ // Get account info
626+ auto info_it = accounts_.find(account_id);
627+ if (info_it == accounts_.end())
628+ {
629+ return;
630+ }
631+ auto info = info_it->second;
632+
633 // Wait until all currently running login sessions are done
634 // (ensures that accounts_ is up to date)
635- std::unique_lock<std::mutex> lock(mutex_);
636+ std::shared_ptr<GMainLoop> event_loop;
637+ event_loop.reset(g_main_loop_new(g_main_context_get_thread_default(), true), g_main_loop_unref);
638+ while(info->session)
639 {
640- std::shared_ptr<GMainLoop> event_loop;
641- event_loop.reset(g_main_loop_new(nullptr, true), g_main_loop_unref);
642- while(logins_busy_ > 0)
643- {
644- // We need to wait inside an event loop to allow for the main application loop to
645- // process its pending events
646- g_timeout_add(100, wake_up_event_loop_cb, event_loop.get());
647- lock.unlock();
648- g_main_loop_run(event_loop.get());
649- lock.lock();
650- }
651+ // We need to wait inside an event loop to allow for the main application loop to
652+ // process its pending events
653+ std::shared_ptr<GSource> source;
654+ source.reset(g_timeout_source_new(10), g_source_unref);
655+ g_source_set_callback(source.get(), wake_up_event_loop_cb, event_loop.get(), NULL);
656+ g_source_attach(source.get(), g_main_context_get_thread_default());
657+
658+ lock.unlock();
659+ g_main_loop_run(event_loop.get());
660+ lock.lock();
661 }
662 }
663
664 void OnlineAccountClientImpl::main_loop_state_notify(bool is_running)
665 {
666+ bool was_running = false;
667+ {
668+ std::lock_guard<std::mutex> lock(mutex_);
669+ was_running = main_loop_is_running_;
670+ }
671+ if (!was_running && is_running)
672+ {
673+ construct();
674+ }
675+ else if (was_running && !is_running)
676+ {
677+ tear_down();
678+ }
679+
680 std::lock_guard<std::mutex> lock(mutex_);
681 main_loop_is_running_ = is_running;
682 cond_.notify_all();
683 }
684
685+std::shared_ptr<AgManager> OnlineAccountClientImpl::manager()
686+{
687+ return manager_;
688+}
689+
690 std::string OnlineAccountClientImpl::service_name()
691 {
692 std::lock_guard<std::mutex> lock(mutex_);
693 return service_name_;
694 }
695
696-void OnlineAccountClientImpl::callback(OnlineAccountClient::ServiceStatus const& service_status)
697+OnlineAccountClient::MainLoopSelect OnlineAccountClientImpl::main_loop_select()
698+{
699+ return main_loop_select_;
700+}
701+
702+void OnlineAccountClientImpl::callback(AccountInfo const* info, std::string const& error)
703 {
704 std::lock_guard<std::mutex> lock(callback_mutex_);
705+ auto service_status = info_to_details(info, error);
706+ if (service_status.service_authenticated && auth_publisher_)
707+ {
708+ auth_publisher_->send_message(details_to_json(service_status));
709+ }
710 if (callback_)
711 {
712 callback_(service_status);
713@@ -408,42 +605,80 @@
714
715 void OnlineAccountClientImpl::remove_account(AgAccountId const& account_id)
716 {
717- std::lock_guard<std::mutex> lock(mutex_);
718+ std::unique_lock<std::mutex> lock(mutex_);
719+
720+ // Get account info
721+ auto info_it = accounts_.find(account_id);
722+ if (info_it == accounts_.end())
723+ {
724+ return;
725+ }
726+ auto info = info_it->second;
727+
728+ // Before we nuke the pointer, ensure that any pending sessions are done
729+ g_signal_handler_disconnect(info->account_service.get(), info->service_update_signal_id_);
730+ clear_session(info.get());
731+ flush_pending_session(account_id, lock);
732+
733+ // Remove account info from accounts_ map
734 accounts_.erase(account_id);
735 }
736
737-bool OnlineAccountClientImpl::inc_logins()
738-{
739- std::lock_guard<std::mutex> lock(mutex_);
740- if (!client_stopping_)
741- {
742- // Increment number of logins busy
743- ++logins_busy_;
744- return true;
745- }
746- // Client is stopping so don't increment
747- return false;
748-}
749-
750-void OnlineAccountClientImpl::dec_logins()
751-{
752- // Decrement number of logins busy
753- std::lock_guard<std::mutex> lock(mutex_);
754- --logins_busy_;
755-}
756-
757 void OnlineAccountClientImpl::main_loop_thread()
758 {
759- // If something goes wrong causing the main loop not to run, the destruction of this pointer
760- // will break the constructor from its wait
761- std::shared_ptr<void> thread_exit_notifier(nullptr, [this](void*){ main_loop_state_notify(true); });
762-
763- // Stick a method call into the main loop to notify the constructor when the main loop begins running
764- g_idle_add(main_loop_is_running_cb, this);
765-
766- // Run the main loop
767- main_loop_.reset(g_main_loop_new(nullptr, true), g_main_loop_unref);
768- g_main_loop_run(main_loop_.get());
769+ // If something goes wrong causing the thread to abort, the destruction of this pointer update the
770+ // main_loop_is_running_ state accordingly.
771+ std::shared_ptr<void> thread_exit_notifier(nullptr, [this](void*){ main_loop_state_notify(false); });
772+
773+ try
774+ {
775+ main_loop_context_.reset(g_main_context_new(), g_main_context_unref);
776+ g_main_context_push_thread_default(main_loop_context_.get());
777+
778+ // Stick a method call into the main loop to notify the constructor when the main loop begins running
779+ g_main_context_invoke(main_loop_context_.get(), main_loop_is_running_cb, this);
780+
781+ // Run the main loop
782+ main_loop_.reset(g_main_loop_new(main_loop_context_.get(), true), g_main_loop_unref);
783+ g_main_loop_run(main_loop_.get());
784+ }
785+ catch (std::exception const& e)
786+ {
787+ std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: " << e.what() << std::endl;
788+ std::lock_guard<std::mutex> lock(mutex_);
789+ thread_exception_ = std::current_exception();
790+ }
791+ catch (...)
792+ {
793+ std::cerr << "OnlineAccountClientImpl::main_loop_thread(): Thread aborted: unknown exception" << std::endl;
794+ std::lock_guard<std::mutex> lock(mutex_);
795+ thread_exception_ = std::current_exception();
796+ }
797+}
798+
799+void OnlineAccountClientImpl::auth_callback(std::string const& details_json)
800+{
801+ OnlineAccountClient::ServiceStatus details = json_to_details(details_json);
802+
803+ // Update account info
804+ {
805+ std::lock_guard<std::mutex> lock(mutex_);
806+ auto info_it = accounts_.find(details.account_id);
807+ if (info_it == accounts_.end())
808+ {
809+ return;
810+ }
811+ auto info = info_it->second;
812+
813+ GVariantDict dict;
814+ g_variant_dict_init(&dict, nullptr);
815+ g_variant_dict_insert(&dict, "AccessToken", "s", details.access_token.c_str());
816+ g_variant_dict_insert(&dict, "TokenSecret", "s", details.token_secret.c_str());
817+ info->session_data.reset(g_variant_ref_sink(g_variant_dict_end(&dict)), free_variant);
818+ }
819+
820+ std::lock_guard<std::mutex> lock(callback_mutex_);
821+ callback_(details);
822 }
823
824 } // namespace internal
825
826=== modified file 'src/scopes/internal/RegistryObject.cpp'
827--- src/scopes/internal/RegistryObject.cpp 2014-09-15 18:33:08 +0000
828+++ src/scopes/internal/RegistryObject.cpp 2014-10-08 10:59:41 +0000
829@@ -33,6 +33,7 @@
830 #include <cassert>
831 #include <fstream>
832 #include <wordexp.h>
833+#include <iostream>
834
835 using namespace std;
836
837@@ -385,7 +386,16 @@
838 desktop_file << "Type=Application" << std::endl;
839 desktop_file << "NoDisplay=true" << std::endl;
840 desktop_file << "Name=" << metadata.display_name() << std::endl;
841- desktop_file << "Icon=" << metadata.icon() << std::endl;
842+ desktop_file << "Icon=";
843+ try
844+ {
845+ desktop_file << metadata.icon();
846+ }
847+ catch (NotFoundException const&)
848+ {
849+ // Icon is optional.
850+ }
851+ desktop_file << endl;
852 desktop_file.close();
853 }
854
855
856=== modified file 'test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp'
857--- test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2014-09-10 08:57:42 +0000
858+++ test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2014-10-08 10:59:41 +0000
859@@ -25,6 +25,8 @@
860
861 TEST(OnlineAccountClient, basic)
862 {
863+ setenv("XDG_RUNTIME_DIR", "/tmp", true);
864+
865 OnlineAccountClient oa_client("com.ubuntu.scopes.youtube_youtube", "sharing", "google");
866 auto service_statuses = oa_client.get_service_statuses();
867 }

Subscribers

People subscribed via source and target branches

to all changes: