Merge lp:~mhr3/unity-scopes-api/always-join-threads into lp:unity-scopes-api/devel

Proposed by Michal Hruby
Status: Merged
Approved by: Michal Hruby
Approved revision: 303
Merged at revision: 301
Proposed branch: lp:~mhr3/unity-scopes-api/always-join-threads
Merge into: lp:unity-scopes-api/devel
Diff against target: 231 lines (+90/-55)
2 files modified
scoperegistry/scoperegistry.cpp (+59/-41)
smartscopesproxy/smartscopesproxy.cpp (+31/-14)
To merge this branch: bzr merge lp:~mhr3/unity-scopes-api/always-join-threads
Reviewer Review Type Date Requested Status
Marcus Tomlinson (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+214802@code.launchpad.net

This proposal supersedes a proposal from 2014-04-08.

Commit message

Always join worker threads.

Description of the change

Always join worker threads. Otherwise if something throws an exception, the thread is destructed without being joined, and that's not a good thing to do.

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
Marcus Tomlinson (marcustomlinson) wrote :

This is cool, although, why not encapsulate completely and make this an actual class with private members and public methods. You could just add a "core::posix::ChildProcess::DeathObserver& death_observer()" method for use in the RegistryObject constructor.

Then, while you're at it, could you please update scoperunner and smartscopesproxy with the same fix. Thanks :)

review: Needs Fixing
Revision history for this message
Michal Hruby (mhr3) wrote :

> This is cool, although, why not encapsulate completely and make this an actual
> class with private members and public methods. You could just add a
> "core::posix::ChildProcess::DeathObserver& death_observer()" method for use in
> the RegistryObject constructor.

It's not generic enough, that's why I didn't encapsulate it more - for example the sigchild trap + deathobserver is only applicable to scope registry, nothing else.

> Then, while you're at it, could you please update scoperunner and
> smartscopesproxy with the same fix. Thanks :)

scoperunner is setup differently and doesn't have the issue, added to smartscopesproxy though.

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

Looks good. Thanks!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michal Hruby (mhr3) wrote :

Pulled the failure from s-jenskins (getting 404 from the public one):

29: Test command: /tmp/buildd/unity-scopes-api-0.4.2+14.04.20140404.2bzr302pkg0trusty157/obj-x86_64-linux-gnu/test/gtest/scopes/Registry/Registry_test
29: Test timeout computed to be: 1500
29: scoperegistry [Registry test]: unity::ResourceException: cannot open scope installation directory "/unused": No such file or directory
29: scoperegistry [Registry test]: could not open OEM installation directory, ignoring OEM scopes
29: scoperegistry [Registry test]: unity::ResourceException: cannot open scope installation directory "/tmp/buildd/.local/share/unity-scopes/": No such file or directory
29: scoperegistry [Registry test]: could not open Click installation directory, ignoring Click scopes
29: scoperegistry [Registry test]: no remote registry configured, only local scopes will be available
29: RegistryObject::ScopeProcess::exec(): Process for scope: "testscopeA" started
29: [==========] Running 1 test from 1 test case.
29: [----------] Global test environment set-up.
29: [----------] 1 test from Registry
29: [ RUN ] Registry.metadata
29: unknown file: Failure
29: C++ exception with description "unity::scopes::TimeoutException: Request timed out after 300 milliseconds" thrown in the test body.
29: [ FAILED ] Registry.metadata (724 ms)
29: [----------] 1 test from Registry (724 ms total)
29:
29: [----------] Global test environment tear-down
29: [==========] 1 test from 1 test case ran. (724 ms total)
29: [ PASSED ] 0 tests.
29: [ FAILED ] 1 test, listed below:
29: [ FAILED ] Registry.metadata

I thought this was supposed to be fixed by that awful hack?

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

Ah right. Could you please make the following change in this branch:
In scoperegistry we need to run:

"middleware->add_state_receiver_object("StateReceiver", registry->state_receiver());"

BEFORE

"middleware->add_registry_object(runtime->registry_identity(), registry);"

This way we ensure that the state receiver is ready to process requests as soon as the registry appears on the middleware.

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

Those lines (mentioned above) are already there, we just need then to be swapped.
Thanks!

303. By Marcus Tomlinson

Init state receiver first

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

That should do it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'scoperegistry/scoperegistry.cpp'
2--- scoperegistry/scoperegistry.cpp 2014-04-04 09:40:40 +0000
3+++ scoperegistry/scoperegistry.cpp 2014-04-09 16:25:44 +0000
4@@ -70,6 +70,58 @@
5 return path;
6 }
7
8+// throwing an exception without joining with a thread is a bad, bad thing to do, so let's RAII to avoid doing it
9+struct SignalThreadWrapper
10+{
11+ std::shared_ptr<core::posix::SignalTrap> termination_trap;
12+ std::shared_ptr<core::posix::SignalTrap> child_trap;
13+ std::unique_ptr<core::posix::ChildProcess::DeathObserver> death_observer;
14+ std::thread termination_trap_worker;
15+ std::thread child_trap_worker;
16+
17+ SignalThreadWrapper() :
18+ // We shutdown the runtime whenever these signals happen.
19+ termination_trap(core::posix::trap_signals_for_all_subsequent_threads(
20+ {
21+ core::posix::Signal::sig_int,
22+ core::posix::Signal::sig_hup,
23+ core::posix::Signal::sig_term
24+ })),
25+ // And we maintain our list of processes with the help of sig_chld.
26+ child_trap(core::posix::trap_signals_for_all_subsequent_threads(
27+ {
28+ core::posix::Signal::sig_chld
29+ })),
30+ // The death observer is required to make sure that we reap all child processes
31+ // whenever multiple sigchld's are compressed together.
32+ death_observer(core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(child_trap)),
33+ // Starting up both traps.
34+ termination_trap_worker([&]() { termination_trap->run(); }),
35+ child_trap_worker([&]() { child_trap->run(); })
36+ {
37+ }
38+
39+ core::Signal<core::posix::Signal>& signal_raised()
40+ {
41+ return termination_trap->signal_raised();
42+ }
43+
44+ ~SignalThreadWrapper()
45+ {
46+ // Stop termination_trap
47+ termination_trap->stop();
48+ if (termination_trap_worker.joinable())
49+ termination_trap_worker.join();
50+
51+ // Please note that the child_trap must only be stopped once the
52+ // termination_trap thread has been joined. We otherwise will encounter
53+ // a race between the middleware shutting down and not receiving sigchld anymore.
54+ child_trap->stop();
55+ if (child_trap_worker.joinable())
56+ child_trap_worker.join();
57+ }
58+};
59+
60 // Return a map of <scope, config_file> pairs for all scopes (Canonical and OEM scopes).
61 // If a Canonical scope is overrideable and the OEM has configured a scope with the
62 // same id, the OEM scope overrides the Canonical one.
63@@ -310,29 +362,7 @@
64
65 try
66 {
67- // We shutdown the runtime whenever these signals happen.
68- auto termination_trap = core::posix::trap_signals_for_all_subsequent_threads(
69- {
70- core::posix::Signal::sig_int,
71- core::posix::Signal::sig_hup,
72- core::posix::Signal::sig_term
73- });
74-
75- // And we maintain our list of processes with the help of sig_chld.
76- auto child_trap = core::posix::trap_signals_for_all_subsequent_threads(
77- {
78- core::posix::Signal::sig_chld
79- });
80-
81- // The death observer is required to make sure that we reap all child processes
82- // whenever multiple sigchld's are compressed together.
83- auto death_observer =
84- core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(
85- child_trap);
86-
87- // Starting up both traps.
88- std::thread termination_trap_worker([termination_trap]() { termination_trap->run(); });
89- std::thread child_trap_worker([child_trap]() { child_trap->run(); });
90+ SignalThreadWrapper signal_handler_wrapper;
91
92 // And finally creating our runtime.
93 RuntimeConfig rt_config(config_file);
94@@ -368,7 +398,7 @@
95
96 // Inform the signal thread that it should shutdown the middleware
97 // if we get a termination signal.
98- termination_trap->signal_raised().connect([middleware](core::posix::Signal signal)
99+ signal_handler_wrapper.signal_raised().connect([middleware](core::posix::Signal signal)
100 {
101 switch(signal)
102 {
103@@ -383,7 +413,7 @@
104 });
105
106 // The registry object stores the local and remote scopes
107- RegistryObject::SPtr registry(new RegistryObject(*death_observer));
108+ RegistryObject::SPtr registry(new RegistryObject(*signal_handler_wrapper.death_observer));
109
110 // Add the metadata for each scope to the lookup table.
111 // We do this before starting any of the scopes, so aggregating scopes don't get a lookup failure if
112@@ -414,14 +444,14 @@
113 load_remote_scopes(registry, middleware, ss_reg_id, ss_reg_endpoint);
114 }
115
116+ // Let's add the registry's state receiver to the middleware so that scopes can inform
117+ // the registry of state changes.
118+ middleware->add_state_receiver_object("StateReceiver", registry->state_receiver());
119+
120 // Now that the registry table is populated, we can add the registry to the middleware, so
121 // it starts processing incoming requests.
122 middleware->add_registry_object(runtime->registry_identity(), registry);
123
124- // We also add the registry's state receiver to the middleware so that scopes can inform
125- // the registry of state changes.
126- middleware->add_state_receiver_object("StateReceiver", registry->state_receiver());
127-
128 // FIXME, HACK HACK HACK HACK
129 // The middleware should spawn scope processes with lookup() on demand.
130 // Because it currently does not have the plumbing, we start every scope immediately.
131@@ -452,18 +482,6 @@
132 // Wait until we are done, which happens if we receive a termination signal.
133 middleware->wait_for_shutdown();
134
135- // Stop termination_trap
136- termination_trap->stop();
137- if (termination_trap_worker.joinable())
138- termination_trap_worker.join();
139-
140- // Please note that the child_trap must only be stopped once the
141- // termination_trap thread has been joined. We otherwise will encounter
142- // a race between the middleware shutting down and not receiving sigchld anymore.
143- child_trap->stop();
144- if (child_trap_worker.joinable())
145- child_trap_worker.join();
146-
147 exit_status = 0;
148 }
149 catch (std::exception const& e)
150
151=== modified file 'smartscopesproxy/smartscopesproxy.cpp'
152--- smartscopesproxy/smartscopesproxy.cpp 2014-03-24 10:19:35 +0000
153+++ smartscopesproxy/smartscopesproxy.cpp 2014-04-09 16:25:44 +0000
154@@ -41,6 +41,35 @@
155 std::cerr << "smartscopesproxy: " << msg << std::endl;
156 }
157
158+struct SignalThreadWrapper
159+{
160+ std::shared_ptr<core::posix::SignalTrap> termination_trap;
161+ std::thread termination_trap_worker;
162+
163+ SignalThreadWrapper() :
164+ termination_trap(core::posix::trap_signals_for_all_subsequent_threads(
165+ {
166+ core::posix::Signal::sig_int,
167+ core::posix::Signal::sig_hup,
168+ core::posix::Signal::sig_term
169+ })),
170+ termination_trap_worker([&]() { termination_trap->run(); })
171+ {
172+ }
173+
174+ core::Signal<core::posix::Signal>& signal_raised()
175+ {
176+ return termination_trap->signal_raised();
177+ }
178+
179+ ~SignalThreadWrapper()
180+ {
181+ termination_trap->stop();
182+ if (termination_trap_worker.joinable())
183+ termination_trap_worker.join();
184+ }
185+};
186+
187 int main(int argc, char* argv[])
188 {
189 if (argc > 1 && (std::string("-h") == argv[1] || std::string("--help") == argv[1]))
190@@ -76,12 +105,7 @@
191
192 try
193 {
194- auto trap = core::posix::trap_signals_for_all_subsequent_threads(
195- {
196- core::posix::Signal::sig_int,
197- core::posix::Signal::sig_hup,
198- core::posix::Signal::sig_term
199- });
200+ SignalThreadWrapper signal_handler;
201
202 ///! TODO: get these from config
203 std::string ss_reg_id = "SSRegistry";
204@@ -102,14 +126,12 @@
205 MiddlewareBase::SPtr reg_mw = reg_rt->factory()->find(ss_reg_id, mw_kind);
206 MiddlewareBase::SPtr scope_mw = scope_rt->factory()->create(ss_scope_id, mw_kind, mw_configfile);
207
208- trap->signal_raised().connect([reg_mw, scope_mw](core::posix::Signal)
209+ signal_handler.signal_raised().connect([reg_mw, scope_mw](core::posix::Signal)
210 {
211 scope_mw->stop();
212 reg_mw->stop();
213 });
214
215- std::thread trap_worker([trap]() { trap->run(); });
216-
217 // Instantiate a SS registry object
218 SSRegistryObject::SPtr reg(new SSRegistryObject(reg_mw, scope_mw->get_scope_endpoint(),
219 http_reply_timeout, ss_reg_refresh_rate));
220@@ -131,11 +153,6 @@
221 scope_mw->wait_for_shutdown();
222 reg_mw->wait_for_shutdown();
223
224- trap->stop();
225-
226- if (trap_worker.joinable())
227- trap_worker.join();
228-
229 exit_status = 0;
230 }
231 catch (std::exception const& e)

Subscribers

People subscribed via source and target branches

to all changes: