Merge lp:~marcustomlinson/unity-scopes-api/scope_process_lifetime into lp:unity-scopes-api/devel

Proposed by Marcus Tomlinson
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 308
Merged at revision: 287
Proposed branch: lp:~marcustomlinson/unity-scopes-api/scope_process_lifetime
Merge into: lp:unity-scopes-api/devel
Diff against target: 2012 lines (+802/-422)
27 files modified
CMakeLists.txt (+1/-1)
debian/changelog (+17/-1)
debian/control (+1/-1)
debian/libunity-scopes0.symbols (+26/-12)
demo/scopes/scope-A/scope-A.ini.in (+1/-1)
demo/scopes/scope-B/scope-B.ini.in (+1/-1)
demo/scopes/scope-C/scope-C.ini.in (+1/-1)
demo/scopes/scope-D/scope-D.ini.in (+1/-1)
demo/scopes/scope-N/scope-N.ini.in (+1/-1)
demo/scopes/scope-S/scope-S.ini.in (+1/-1)
doc/tutorial.dox (+1/-1)
include/unity/scopes/internal/RegistryObject.h (+66/-18)
include/unity/scopes/internal/RegistryObjectBase.h (+2/-2)
include/unity/scopes/internal/RuntimeImpl.h (+2/-0)
include/unity/scopes/internal/smartscopes/SSRegistryObject.h (+3/-3)
scoperegistry/CMakeLists.txt (+0/-1)
scoperegistry/SignalThread.cpp (+0/-121)
scoperegistry/SignalThread.h (+0/-55)
scoperegistry/scoperegistry.cpp (+72/-21)
scoperunner/scoperunner.cpp (+32/-2)
smartscopesproxy/smartscopesproxy.cpp (+24/-2)
src/scopes/ScopeMetadata.cpp (+1/-0)
src/scopes/internal/RegistryObject.cpp (+293/-134)
src/scopes/internal/RuntimeImpl.cpp (+9/-0)
src/scopes/internal/smartscopes/SSRegistryObject.cpp (+9/-8)
test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp (+236/-32)
test/gtest/scopes/internal/zmq_middleware/RegistryI/TestRegistry.ini.in (+1/-1)
To merge this branch: bzr merge lp:~marcustomlinson/unity-scopes-api/scope_process_lifetime
Reviewer Review Type Date Requested Status
Paweł Stołowski (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Michal Hruby (community) Approve
Michi Henning Pending
Review via email: mp+209886@code.launchpad.net

Commit message

Updated RegistryObject to monitor and maintain all scope process states, as to properly launch and shutdown processes accordingly.

Description of the change

Updated RegistryObject to monitor and maintain all scope process states using process-cpp.

On locate(), RegistryObject checks the process' current running state and executes the appropriate action: stopping -> wait, running -> return, stopped -> start.

A background "death observer" thread (provided by process-cpp) is instantiated on entry to scoperegistry and provided to RegistryObject in order to catch child death events asynchronously.

The inherited sig mask from scoperegistry (sigterm, sigchld, etc.) is cleared on entry to scoperunner in order to provide scopes with a clean runtime environment.

The correct tear-down of scope processes is handled on destruction of RegistryObject.

Slow reacting processes are handled with appropriate timeouts.

Tests added to verify new RegistryObject functionality.

To post a comment you must log in.
Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

The changes to process-cpp required for this branch have now landed. Ready for review.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
266. By Marcus Tomlinson

Fixed RegistryI_test

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

611 + // broadcast message to all scope_processes_

Why do we do that? Can you add a comment in the code explaining it?

review: Needs Information
Revision history for this message
Paweł Stołowski (stolowski) wrote :

661 +void RegistryObject::ScopeProcess::exec()
662 +{
..
665 + if (state() == ScopeProcess::Running)
..
670 + else if (state() == ScopeProcess::Stopping)
..
681 + update_state(Starting);

Shouldn't we be locking state for the entire scope of this method, rather then only when reading / updating the state? I think we have a potential race in exec() if there are two concurrent calls to the registry for same scope id - between the time we examine state and update it, both callers can find out the scope is not yet running (nor stopping) and will be trying to start scope proces)?

720 + // 4. add the scope process to the death observer
721 + core::posix::ChildProcess::DeathObserver::instance().add(process_);
722 +}

Shouldn't this happen early (before we start the process)? What if it dies shortly after we check that the state is Running in line #706, but before we register the observer?

review: Needs Information
Revision history for this message
Paweł Stołowski (stolowski) wrote :

711 + if (!wait_for_state(ScopeProcess::Running, 1000))
712 + {
713 + kill();
714 + throw unity::ResourceException("RegistryObject::ScopeProcess: exec() aborted. Scope: \""
715 + + exec_data_.scope_id + "\" took too long to start.");
716 + }

Please consider catching exception from kill() here.

Revision history for this message
Paweł Stołowski (stolowski) wrote :

694 + process_ = core::posix::exec(program, argv, env, core::posix::StandardStream::empty);

I think we should pass stdout|stderr, or otherwise we'll loose all error messages from scopes, no?

review: Needs Information
Revision history for this message
Michi Henning (michihenning) wrote :

Currently, locate() locks on entry. If we have several requests to locate() concurrently, such as when an aggregator wants to talk to several children, start-up of the children will be serialised, as far as I can see: the second scope won't be exec'd until after the first scope has done its exec() and is ready. I think we need to be able to fire execs concurrently, otherwise things will take too long.

On the binding side, I'm planning to wait at most 1.5 seconds for the exec to complete before giving up. So, the sum of start-up times of any scopes being activated together must not exceed that limit.

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

> 720 + // 4. add the scope process to the death observer
> 721 +
> core::posix::ChildProcess::DeathObserver::instance().add(process_);
> 722 +}
>
> Shouldn't this happen early (before we start the process)? What if it dies
> shortly after we check that the state is Running in line #706, but before we
> register the observer?

There is never a time where you can add a ChildProcess to the death observer early enough. See, a ChildProcess can only be constructed after it's process is started, which is already too late as the process may have crashed immediately (hence the death observer misses that signal).

This was sorted out in process-cpp so that when an already dead process is added it will fire the on_process_death signal, which in turn, will then execute the correct ChildProcess tear down routine.

267. By Marcus Tomlinson

Merged devel

268. By Marcus Tomlinson

Beefed up the comments in RegistryObject::on_process_death

269. By Marcus Tomlinson

Fixed warning in documentation.

270. By Marcus Tomlinson

Reduced mutexes in RegistryObject::ScopeProcess to a single process_mutex_ to better safety-proof and simplify concurrent request handling.

271. By Marcus Tomlinson

Give stdout|stderr handles to child processes in order to pipe their outputs to the registry's output stream.

272. By Marcus Tomlinson

Reduced scope of the mutex lock in RegistryObject::locate() as to avoid a pile-up on multiple locate() requests for different scopes.

273. By Marcus Tomlinson

Catch exceptions from kill() calls in ScopeProcess::exec().

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
274. By Marcus Tomlinson

If a kill hard fails, detach from the process and move on.

275. By Marcus Tomlinson

DeathObserver::quit() throws, so try...catch added to the RegistryObject destructor.

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

Alright, I think I've addressed all review comments so far.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
276. By Marcus Tomlinson

Updated symbols file.

Revision history for this message
Paweł Stołowski (stolowski) wrote :

786 + ///! TODO: This should not be here. A ready signal from the scope should trigger "running".

This would be nice to have; any reason no to fix it right away?

review: Needs Information
Revision history for this message
Michi Henning (michihenning) wrote :

> This would be nice to have; any reason no to fix it right away?

Mea culpa. The communication from the scope to the registry doesn't exist yet :-(

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

> 786 + ///! TODO: This should not be here. A ready signal from the scope
> should trigger "running".
>
> This would be nice to have; any reason no to fix it right away?

This will be the next branch I'm working on. It's not a trivial change so I think it's better we separate them.

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

in_lock_* methods renamed to *_unlocked

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

So, I tried to run this against the tests in lp:unity-scopes-shell (because yey for no end-to-end registry tests in -api), and it was behaving strangely - running make test sometimes finished in 1.0seconds, other times in 1.8 seconds, and sometimes after 30seconds.

Possibly (just) a cleanup issue as when looking at the processes when the test was taking 30seconds to finish, the scope process was a zombie after about a second of running the tests.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
278. By Marcus Tomlinson

Detach from death_observer_thread_ if quit throws.

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

> So, I tried to run this against the tests in lp:unity-scopes-shell (because
> yey for no end-to-end registry tests in -api), and it was behaving strangely -
> running make test sometimes finished in 1.0seconds, other times in 1.8
> seconds, and sometimes after 30seconds.
>
> Possibly (just) a cleanup issue as when looking at the processes when the test
> was taking 30seconds to finish, the scope process was a zombie after about a
> second of running the tests.

Ok so firstly, the 1 to 2 second delay I think is a scoperunner issue. Sometimes a scoperunner, after being "kill" signalled, hangs around like a zombie. I send a kill signal and wait (currently for 1s) for the process to die. If it doesn't die within that period, I print the following message, and move on (there is little more I can do at this point):

"Scope: "x" is taking longer than expected to terminate (This process is likely to close upon termination of the parent application)."

I think the 30s shutdown (which I believe is actually just a deadlock timing out in your test), is due to the death observer thread missing the quit() request in the RegistryObject destructor. I'm busy fixing that in process-cpp now.

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 :

Besides the deadlock issue (a problem with process-cpp), my only request for now as discussed on the standup would be to have a stress test that starts a bunch of scopes and tries to start same scope multiple times (simulate a case where e.g. a few clients try to hit same scope at the same time, making sure we have just one such scope process running).

279. By Marcus Tomlinson

Added test for RegistryObject::locate

280. By Marcus Tomlinson

Added checks for process count during locate test.

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

> Besides the deadlock issue (a problem with process-cpp), my only request for
> now as discussed on the standup would be to have a stress test that starts a
> bunch of scopes and tries to start same scope multiple times (simulate a case
> where e.g. a few clients try to hit same scope at the same time, making sure
> we have just one such scope process running).

Alright, the process-cpp issue has been MR-ed and will soon be landing.
I also added the requested tests.

281. By Marcus Tomlinson

Updated symbols

282. By Marcus Tomlinson

Merged devel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
283. By Marcus Tomlinson

Some formatting fixes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
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 :

Thanks for the test, great stuff!!

One more question re the test - could add one more case to it: after all 6 scopes get started, kill/stop one of them with SIGQUIT/SIGINT etc., then ensure the process is gone (5 scopes active), and then locate it again to verify it's started again?

review: Needs Information
284. By Marcus Tomlinson

Added checks for scope process deaths to locate test.

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

> Thanks for the test, great stuff!!
>
> One more question re the test - could add one more case to it: after all 6
> scopes get started, kill/stop one of them with SIGQUIT/SIGINT etc., then
> ensure the process is gone (5 scopes active), and then locate it again to
> verify it's started again?

Alright, done. Looking into this actually revealed a mistake in the registry where removing a scope didn't kill it's process. Fixed that too.

285. By Marcus Tomlinson

Updated kill() logic to first try terminate the scope gracefully, then kill only if it takes too long to close.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
286. By Marcus Tomlinson

Wait for scope to properly die after calling kill in locate test.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

This looks good to me! Thanks!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
287. By Marcus Tomlinson

Wait for the SIGCHLD signal to reach the registry

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 :
Download full text (5.5 KiB)

Hmm, I'm having issues when running scoperegistry + demo scopes from this branch. Stopping the registry gives errors on some scope, and then trying to run registry again gives errors about some addresses still in use.

pawel@trusty ~/src/ubuntu/unity-scopes-api/scope-cfg-runner/build/demo $ ../scoperegistry/scoperegistry Runtime.ini
scoperegistry: unity::ResourceException: cannot open scope installation directory "/custom/usr/lib/x86_64-linux-gnu/unity/scopes": No such file or directory
scoperegistry: could not open OEM installation directory, ignoring OEM scopes
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-A" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-B" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-C" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-D" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-N" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-S" started
^CRegistryObject::ScopeProcess::in_lock_kill(): Failed to kill scope: "scope-S"
RegistryObject::ScopeProcess::~ScopeProcess(): unity::ResourceException: Scope: "scope-S" is taking longer than expected to terminate (This process is likely to close upon termination of the parent application).
pawel@trusty ~/src/ubuntu/unity-scopes-api/scope-cfg-runner/build/demo $ ../scoperegistry/scoperegistry Runtime.ini
scoperegistry: unity::ResourceException: cannot open scope installation directory "/custom/usr/lib/x86_64-linux-gnu/unity/scopes": No such file or directory
scoperegistry: could not open OEM installation directory, ignoring OEM scopes
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-A" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-B" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-C" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-D" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-N" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-S" started
~ObjectAdapter(): exception from shutdown(): unity::scopes::MiddlewareException: Object adapter in Failed state (adapter: scope-B)
~ObjectAdapter(): exception from wait_for_shutdown(): unity::scopes::MiddlewareException: Object adapter in Failed state (adapter: scope-B)
unexpected exception in add_scope_object(): unity::scopes::MiddlewareException: ObjectAdapter::run_workers(): broker thread failure (adapter: scope-B):
    unity::scopes::MiddlewareException: ObjectAdapter: broker thread failure (adapter: scope-B): address in use: ipc:///tmp/scope-B
~ObjectAdapter(): exception from shutdown(): unity::scopes::MiddlewareException: Object adapter in Failed state (adapter: scope-A)
~ObjectAdapter(): exception from wait_for_shutdown(): unity::scopes::MiddlewareException: Object adapter in Failed state (adapter: scope-A)
unexpected exception in add_scope_object(): unity::scopes::MiddlewareException: ObjectAdapter::run_workers(): broker thread failure (adapt...

Read more...

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

948 + while (state_ != state && time_left > 0)
949 + {
950 + auto start = std::chrono::high_resolution_clock::now();
951 + state_change_cond_.wait_for(lock, std::chrono::milliseconds(time_left));
952 +
953 + // update time left
954 + time_left -= std::chrono::duration_cast<std::chrono::milliseconds>(
955 + std::chrono::high_resolution_clock::now() - start).count();
956 + }

Why so complicated? condvar.wait_for(lock, timeout, [](){return state_ == state;}); Would do, no?

Why don't the clear_handle_unlocked and update_state_unlocked don't have the unique_lock as a param? Would make the semantics safer.

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

942 +bool RegistryObject::ScopeProcess::wait_for_state_unlocked(std::unique_lock<std::mutex>& lock,

Also, this is a bit confusing, has the _unlocked suffix, yet it drops the mutex, when waiting for the condvar.

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

> 948 + while (state_ != state && time_left > 0)
> 949 + {
> 950 + auto start = std::chrono::high_resolution_clock::now();
> Why so complicated? condvar.wait_for(lock, timeout, [](){return state_ ==
> state;}); Would do, no?

No. [](){return state_ == state;} is just a predicate to check before locking, which is handled by the if() statement anyway. This wait_for can be woken by any state change, not necessarily the one we want, that's why I need to return to wait_for in a loop until we do get the state we want

> Why don't the clear_handle_unlocked and update_state_unlocked don't have the
> unique_lock as a param? Would make the semantics safer.

Those methods take in the lock because they need to use it in the wait_for, the others don't use it they just need it locked before they're called.

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

> 942 +bool RegistryObject::ScopeProcess::wait_for_state_unlocked(std::uniqu
> e_lock<std::mutex>& lock,
>
> Also, this is a bit confusing, has the _unlocked suffix, yet it drops the
> mutex, when waiting for the condvar.

Hense the original "in_lock_*" prefix. These methods simply must be called while process_mutex_ is locked.

288. By Marcus Tomlinson

Kill scope processes one-by-one in the RegistryObject destructor (Just calling clear on the map is not safe)

289. By Marcus Tomlinson

Cleaned up wait_for_state_unlocked() logic.

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

> > 942 +bool
> RegistryObject::ScopeProcess::wait_for_state_unlocked(std::uniqu
> > e_lock<std::mutex>& lock,
> >
> > Also, this is a bit confusing, has the _unlocked suffix, yet it drops the
> > mutex, when waiting for the condvar.
>
> Hense the original "in_lock_*" prefix. These methods simply must be called
> while process_mutex_ is locked.

Then I'd suggest using overloaded method instead (just for this): one wait_for_state() without the unique_lock param and one with it (of course one will call the other).

290. By Marcus Tomlinson

Cleaned up kill_unlocked() method

291. By Marcus Tomlinson

wait_for_state_unlock() and kill_unlock() renamed to wait_for_state() and kill() as the lock is implicitly required.

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

> 948 + while (state_ != state && time_left > 0)
> 949 + {
> 950 + auto start = std::chrono::high_resolution_clock::now();
> Why so complicated? condvar.wait_for(lock, timeout, [](){return state_ ==
> state;}); Would do, no?

done.

> Then I'd suggest using overloaded method instead (just for this): one
> wait_for_state() without the unique_lock param and one with it (of course one
> will call the other).

done.

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

> Hmm, I'm having issues when running scoperegistry + demo scopes from this
> branch. Stopping the registry gives errors on some scope, and then trying to
> run registry again gives errors about some addresses still in use.

I'm really sorry about this pawel, this was due to a late change I made to the RegistryObject destructor. I've fixed this (again).

292. By Marcus Tomlinson

Updated symbols

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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 :

There is still something weird going on when I stop scoperegistry with Ctrl+C.. No more leftover proccesses, but instead every single demo scope gives:

^CRegistryObject::ScopeProcess::kill(): Scope: "scope-A" is taking longer than expected to terminate gracefully. Killing the process instead.
RegistryObject::ScopeProcess::kill(): Scope: "scope-B" is taking longer than expected to terminate gracefully. Killing the process instead.
RegistryObject::ScopeProcess::kill(): Scope: "scope-C" is taking longer than expected to terminate gracefully. Killing the process instead.
RegistryObject::ScopeProcess::kill(): Scope: "scope-D" is taking longer than expected to terminate gracefully. Killing the process instead.
RegistryObject::ScopeProcess::kill(): Scope: "scope-N" is taking longer than expected to terminate gracefully. Killing the process instead.
RegistryObject::ScopeProcess::kill(): Scope: "scope-S" is taking longer than expected to terminate gracefully. Killing the process instead.

These scope are very simple, so I find it hard to believe... On a side note, all this happens in sequence, so it takes (num_of_scopes * timeout) for shutdown (but I'm not going to block on this problem right now).

Another question: is this still expected that all scopes are automatically started when registry is started? Wasn't this branch supposed to change that so that first call to locate() starts a scope?

BTW, I'm running up-to-date trusty, libprocess-cpp is 0.0.1+14.04.20140317-0ubuntu1.

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

I can confirm what Pawel is saying, there's still some weirdness going on, namely:

1) scoperunners don't terminate after receiving SIGTERM
2) I do not see the log line "RegistryObject::ScopeProcess::on_process_death(): ..." after sending the scoperunner process SIGKILL.
3) After sending SIGKILL I still see the process in ps list as a zombie (ie waitpid not called)
4) Sending a request after sending SIGKILL to scoperunner doesn't restart the scope (although I guess that's because the registry doesn't realize it's dead (see 2)). Is that supposed to work with this branch?

review: Needs Fixing
293. By Marcus Tomlinson

Added internal SigTermHandler class to handle SIGTERM signal in scoperegistry, scoperunner and smartscopesproxy.

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

> Another question: is this still expected that all scopes are automatically
> started when registry is started? Wasn't this branch supposed to change that
> so that first call to locate() starts a scope?

Yes, unfortunately that's how we still need to do it. This requires rebinding logic to be added to ZmqObjectProxy::invoke_. I will be working on that and the scope-to-reg signals over the next 2 weeks.

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

> I can confirm what Pawel is saying, there's still some weirdness going on,
> namely:
>
> 1) scoperunners don't terminate after receiving SIGTERM

fixed.

> 2) I do not see the log line
> "RegistryObject::ScopeProcess::on_process_death(): ..." after sending the
> scoperunner process SIGKILL.
> 3) After sending SIGKILL I still see the process in ps list as a zombie (ie
> waitpid not called)

There seems to be yet another iffy situation in process-cpp where it just misses SIGCHLD signals :( I've debugged scoperunner on killing it and it exits properly, the ::poll in the death observer just doesn't react to it?? I don't know if you have any ideas?

> 4) Sending a request after sending SIGKILL to scoperunner doesn't restart the
> scope (although I guess that's because the registry doesn't realize it's dead
> (see 2)). Is that supposed to work with this branch?

Re-binding doesn't work yet (see last reply to Pawel)

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

8 - (c++)"unity::scopes::Runtime::create(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
10 + (c++)"unity::scopes::Runtime::create(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0replaceme

Something wrong there, pls revert (did gensymbols decide to sort the symbols differently this time?)

294. By Marcus Tomlinson

Reverted Runtime::create replacement in symbols

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

> 8 - (c++)"unity::scopes::Runtime::create(std::basic_string<char,
> std::char_traits<char>, std::allocator<char> > const&)@Base"
> 0.4.0+14.04.20140312.1
> 10 + (c++)"unity::scopes::Runtime::create(std::basic_string<char,
> std::char_traits<char>, std::allocator<char> > const&)@Base" 0replaceme
>
> Something wrong there, pls revert (did gensymbols decide to sort the symbols
> differently this time?)

K, reverted that one. Yeah, looks like gensymbols reordered one other symbol too, but it's a "0replaceme" one anyways.

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

Updated RegistryObject::ScopeProcess::state() to const

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
296. By Marcus Tomlinson

Updated symbols

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

Merged lp:~thomas-voss/unity-scopes-api/socpe_process_lifetime

298. By Marcus Tomlinson

Merged devel and fixed conflicts

299. By Marcus Tomlinson

Fixed resolve mistake

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

Alright, this is ready for review again.
Note: lp:~thomas-voss/process-cpp/fix-death-observer is required in conjunction with this branch.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
300. By Marcus Tomlinson

Fixed bzr bd issues

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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 :

1932 + unity::scopes::internal::SigTermHandler::*;

Please remove.

Otherwise it's looking fine, but needs to wait for process-cpp branches to land first. Is there going to be version bump in process-cpp? Build deps should be updated here.

review: Needs Fixing
301. By Marcus Tomlinson

Added comment to explain tear down sequence.

302. By Marcus Tomlinson

Removed SigTermHandler from unity-scopes.map

303. By Marcus Tomlinson

Updated build deps for process-cpp 1.0.0

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

> 1932 + unity::scopes::internal::SigTermHandler::*;
>
> Please remove.

ah yes, nice catch. done.

> Otherwise it's looking fine, but needs to wait for process-cpp branches to
> land first. Is there going to be version bump in process-cpp? Build deps
> should be updated here.

build deps updated.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
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 :

Looks good to me now, still need to wait for process-cpp though.

review: Approve
304. By Marcus Tomlinson

Merged devel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
305. By Marcus Tomlinson

Merged trunk and resolved conflicts

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
306. By Marcus Tomlinson

Clear the sig mask (inherited from scoperegistry) on entry.

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

Merged trunk

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 :

Hmm, still no luck with it; I'm testing it with demo scopes and kill scope-A manually with kill <pid of scope>:

pawel@trusty ~/src/ubuntu/unity-scopes-api/scope_process_lifetime/build/demo $ ../scoperegistry/scoperegistry Runtime.ini
scoperegistry: unity::ResourceException: cannot open scope installation directory "/custom/usr/lib/x86_64-linux-gnu/unity/scopes": No such file or directory
scoperegistry: could not open OEM installation directory, ignoring OEM scopes
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-A" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-B" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-C" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-D" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-N" started
RegistryObject::ScopeProcess::exec(): Process for scope: "scope-S" started
RegistryObject::ScopeProcess::on_process_death(): Process for scope: "scope-A" terminated

The death of scope-A is correctly detected, but I tried several times to query it after killing it, and getting this:

pawel@trusty ~/src/ubuntu/unity-scopes-api/scope_process_lifetime/build/demo $ ./client scope-A q
Scope metadata:
        scope_id: scope-A
        display_name: scope-A.DisplayName
        description: scope-A.Description
        art: scope-A.Art
        icon: scope-A.Icon
        search_hint: scope-A.SearchHint
        hot_key: scope-A.HotKey
query complete, status: error: unity::scopes::TimeoutException: Request timed out after 300 milliseconds
unity::scopes::TimeoutException: Request timed out after 300 milliseconds

And the scope is not started.

NB, I'm running it with the fix_env_var_split branc of libprocess-cpp.

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

> Hmm, still no luck with it; I'm testing it with demo scopes and kill scope-A
> manually with kill <pid of scope>:
>
> pawel@trusty ~/src/ubuntu/unity-scopes-api/scope_process_lifetime/build/demo $
> ../scoperegistry/scoperegistry Runtime.ini
> scoperegistry: unity::ResourceException: cannot open scope installation
> directory "/custom/usr/lib/x86_64-linux-gnu/unity/scopes": No such file or
> directory
> scoperegistry: could not open OEM installation directory, ignoring OEM scopes
> RegistryObject::ScopeProcess::exec(): Process for scope: "scope-A" started
> RegistryObject::ScopeProcess::exec(): Process for scope: "scope-B" started
> RegistryObject::ScopeProcess::exec(): Process for scope: "scope-C" started
> RegistryObject::ScopeProcess::exec(): Process for scope: "scope-D" started
> RegistryObject::ScopeProcess::exec(): Process for scope: "scope-N" started
> RegistryObject::ScopeProcess::exec(): Process for scope: "scope-S" started
> RegistryObject::ScopeProcess::on_process_death(): Process for scope: "scope-A"
> terminated
>
> The death of scope-A is correctly detected, but I tried several times to query
> it after killing it, and getting this:
>
> pawel@trusty ~/src/ubuntu/unity-scopes-api/scope_process_lifetime/build/demo $
> ./client scope-A q
> Scope metadata:
> scope_id: scope-A
> display_name: scope-A.DisplayName
> description: scope-A.Description
> art: scope-A.Art
> icon: scope-A.Icon
> search_hint: scope-A.SearchHint
> hot_key: scope-A.HotKey
> query complete, status: error: unity::scopes::TimeoutException: Request timed
> out after 300 milliseconds
> unity::scopes::TimeoutException: Request timed out after 300 milliseconds
>
> And the scope is not started.
>
> NB, I'm running it with the fix_env_var_split branc of libprocess-cpp.

As discussed on IRC, this branch essentially just replicates what we already had using process-cpp. This implementation is really just a more correct / cleaner version of the previous one. We now have timeouts on process operations, terminate using sigterm instead of jumping straight to sigkill, keep track of all process states, and one could argue that we now have a better separation of concerns (between process-cpp and unity-scopes-api).

Other items on the todo list to come in other branches are:
1. re-binding logic on failure to execute a query (call locate() to ensure a scope is running).
2. introduce scope-to-registry signals to inform registry of changes in the scope's running state.
3. address idle timeouts of hanging / non-responding scopes.

308. By Marcus Tomlinson

Don't terminate scoperunner on sigint, as this disables one from debugging their scope.

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 :

OK, I run this on the phone and didn't spot any regressions. Let's get it in.

review: Approve

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-03-12 16:41:30 +0000
3+++ CMakeLists.txt 2014-03-28 15:41:24 +0000
4@@ -56,7 +56,7 @@
5 include(FindPkgConfig)
6 find_package(Boost COMPONENTS regex serialization REQUIRED)
7 pkg_check_modules(UNITY_API libunity-api>=0.1.1 REQUIRED)
8-pkg_check_modules(PROCESS_CPP process-cpp REQUIRED)
9+pkg_check_modules(PROCESS_CPP process-cpp>=1.0.0 REQUIRED)
10 pkg_check_modules(LTTNG_UST lttng-ust REQUIRED)
11 pkg_check_modules(LIBURCU_BP liburcu-bp REQUIRED)
12 find_program(LTTNG_EXECUTABLE lttng)
13
14=== modified file 'debian/changelog'
15--- debian/changelog 2014-03-20 12:27:33 +0000
16+++ debian/changelog 2014-03-28 15:41:24 +0000
17@@ -1,3 +1,9 @@
18+unity-scopes-api (0.4.1+14.04.20140326.1-0ubuntu1) trusty; urgency=low
19+
20+ * New rebuild forced
21+
22+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 26 Mar 2014 17:57:37 +0000
23+
24 unity-scopes-api (0.4.1-0ubuntu1) UNRELEASED; urgency=medium
25
26 [ Pawel Stolowski ]
27@@ -5,12 +11,22 @@
28
29 -- Pawel Stolowski <pawel.stolowski@ubuntu.com> Wed, 12 Mar 2014 17:03:16 +0100
30
31+unity-scopes-api (0.4.0+14.04.20140324-0ubuntu1) trusty; urgency=low
32+
33+ [ Michal Hruby ]
34+ * Backport invalidation raciness fix.
35+
36+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 24 Mar 2014 13:06:04 +0000
37+
38 unity-scopes-api (0.4.0+14.04.20140319-0ubuntu1) trusty; urgency=low
39
40 [ Michal Hruby ]
41 * Backport the invalidation fix.
42
43- -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 19 Mar 2014 16:39:49 +0000
44+ [ Ubuntu daily release ]
45+ * debian/*symbols: auto-update new symbols to released version
46+
47+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 24 Mar 2014 13:06:02 +0000
48
49 unity-scopes-api (0.4.0+14.04.20140312.2-0ubuntu1) trusty; urgency=low
50
51
52=== modified file 'debian/control'
53--- debian/control 2014-03-03 11:50:58 +0000
54+++ debian/control 2014-03-28 15:41:24 +0000
55@@ -9,7 +9,7 @@
56 pkg-config,
57 python3,
58 capnproto,
59- libprocess-cpp-dev,
60+ libprocess-cpp-dev (>= 1.0.0),
61 libunity-api-dev (>= 7.80.5~),
62 libboost-regex-dev,
63 libboost-serialization-dev,
64
65=== modified file 'debian/libunity-scopes0.symbols'
66--- debian/libunity-scopes0.symbols 2014-03-21 15:18:32 +0000
67+++ debian/libunity-scopes0.symbols 2014-03-28 15:41:24 +0000
68@@ -279,7 +279,7 @@
69 (c++)"unity::scopes::Result::operator=(unity::scopes::Result&&)@Base" 0.4.0+14.04.20140312.1
70 (c++)"unity::scopes::Result::operator=(unity::scopes::Result const&)@Base" 0.4.0+14.04.20140312.1
71 (c++)"unity::scopes::Result::operator[](std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
72- (c++)"unity::scopes::Runtime::create_scope_runtime(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0replaceme
73+ (c++)"unity::scopes::Runtime::create_scope_runtime(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140324
74 (c++)"unity::scopes::Runtime::create(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
75 (c++)"unity::scopes::Runtime::destroy()@Base" 0.4.0+14.04.20140312.1
76 (c++)"unity::scopes::Runtime::run_scope(unity::scopes::ScopeBase*)@Base" 0.4.0+14.04.20140312.1
77@@ -389,11 +389,9 @@
78 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::activate(unity::scopes::Result const&, unity::scopes::ActionMetadata const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0.4.0+14.04.20140312.1
79 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::SSScopeObject(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::shared_ptr<unity::scopes::internal::MiddlewareBase>, std::shared_ptr<unity::scopes::internal::smartscopes::SSRegistryObject>)@Base" 0.4.0+14.04.20140312.1
80 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::~SSScopeObject()@Base" 0.4.0+14.04.20140312.1
81- (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::get_metadata(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
82 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::refresh_thread()@Base" 0.4.0+14.04.20140312.1
83 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::get_remote_scopes()@Base" 0.4.0+14.04.20140312.1
84- (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::add(unity::scopes::internal::smartscopes::RemoteScope const&, unity::scopes::ScopeMetadata const&, std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, unity::scopes::ScopeMetadata, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, unity::scopes::ScopeMetadata> > >&, std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >&)@Base" 0replaceme
85- (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::list()@Base" 0.4.0+14.04.20140312.1
86+ (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::add(unity::scopes::internal::smartscopes::RemoteScope const&, unity::scopes::ScopeMetadata const&, std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, unity::scopes::ScopeMetadata, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, unity::scopes::ScopeMetadata> > >&, std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >&)@Base" 0.4.0+14.04.20140324
87 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::locate(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
88 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::SSRegistryObject(std::shared_ptr<unity::scopes::internal::MiddlewareBase>, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int, unsigned int, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool)@Base" 0.4.0+14.04.20140312.1
89 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::~SSRegistryObject()@Base" 0.4.0+14.04.20140312.1
90@@ -402,17 +400,25 @@
91 (c++)"unity::scopes::internal::RegistryConfig::REGISTRY_CONFIG_GROUP@Base" 0.4.0+14.04.20140312.1
92 (c++)"unity::scopes::internal::RegistryConfig::RegistryConfig(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
93 (c++)"unity::scopes::internal::RegistryConfig::~RegistryConfig()@Base" 0.4.0+14.04.20140312.1
94- (c++)"unity::scopes::internal::RegistryObject::spawn_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
95- (c++)"unity::scopes::internal::RegistryObject::get_metadata(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
96- (c++)"unity::scopes::internal::RegistryObject::kill_process(int)@Base" 0.4.0+14.04.20140312.1
97- (c++)"unity::scopes::internal::RegistryObject::add_local_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unity::scopes::ScopeMetadata const&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&)@Base" 0.4.0+14.04.20140312.1
98+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::on_process_death(int)@Base" 0replaceme
99+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::clear_handle_unlocked()@Base" 0replaceme
100+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::update_state_unlocked(unity::scopes::internal::RegistryObject::ScopeProcess::ProcessState)@Base" 0replaceme
101+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::exec(core::posix::ChildProcess::DeathObserver&)@Base" 0replaceme
102+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::kill(std::unique_lock<std::mutex>&)@Base" 0replaceme
103+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::kill()@Base" 0replaceme
104+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::ScopeProcess(unity::scopes::internal::RegistryObject::ScopeExecData)@Base" 0replaceme
105+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::ScopeProcess(unity::scopes::internal::RegistryObject::ScopeProcess const&)@Base" 0replaceme
106+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::ScopeProcess(unity::scopes::internal::RegistryObject::ScopeExecData)@Base" 0replaceme
107+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::ScopeProcess(unity::scopes::internal::RegistryObject::ScopeProcess const&)@Base" 0replaceme
108+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::~ScopeProcess()@Base" 0replaceme
109+ (c++)"unity::scopes::internal::RegistryObject::ScopeExecData::~ScopeExecData()@Base" 0replaceme
110+ (c++)"unity::scopes::internal::RegistryObject::add_local_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unity::scopes::ScopeMetadata const&, unity::scopes::internal::RegistryObject::ScopeExecData const&)@Base" 0replaceme
111+ (c++)"unity::scopes::internal::RegistryObject::is_scope_running(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0replaceme
112+ (c++)"unity::scopes::internal::RegistryObject::on_process_death(core::posix::Process const&)@Base" 0replaceme
113 (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
114 (c++)"unity::scopes::internal::RegistryObject::set_remote_registry(std::shared_ptr<unity::scopes::internal::MWRegistry> const&)@Base" 0.4.0+14.04.20140312.1
115- (c++)"unity::scopes::internal::RegistryObject::list()@Base" 0.4.0+14.04.20140312.1
116 (c++)"unity::scopes::internal::RegistryObject::locate(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
117- (c++)"unity::scopes::internal::RegistryObject::is_dead(int)@Base" 0.4.0+14.04.20140312.1
118- (c++)"unity::scopes::internal::RegistryObject::shutdown()@Base" 0.4.0+14.04.20140312.1
119- (c++)"unity::scopes::internal::RegistryObject::RegistryObject()@Base" 0.4.0+14.04.20140312.1
120+ (c++)"unity::scopes::internal::RegistryObject::RegistryObject(core::posix::ChildProcess::DeathObserver&)@Base" 0replaceme
121 (c++)"unity::scopes::internal::RegistryObject::~RegistryObject()@Base" 0.4.0+14.04.20140312.1
122 (c++)"unity::scopes::internal::MiddlewareFactory::MiddlewareData::~MiddlewareData()@Base" 0.4.0+14.04.20140312.1
123 (c++)"unity::scopes::internal::MiddlewareFactory::to_kind(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
124@@ -580,6 +586,7 @@
125 (c++)"unity::scopes::Category::icon() const@Base" 0.4.0+14.04.20140312.1
126 (c++)"unity::scopes::Category::title() const@Base" 0.4.0+14.04.20140312.1
127 (c++)"unity::scopes::Category::serialize() const@Base" 0.4.0+14.04.20140312.1
128+ (c++)"unity::scopes::internal::RuntimeImpl::configfile() const@Base" 0replaceme
129 (c++)"unity::scopes::internal::RuntimeImpl::reply_reaper() const@Base" 0.4.0+14.04.20140312.1
130 (c++)"unity::scopes::internal::RuntimeImpl::proxy_to_string(std::shared_ptr<unity::scopes::Object> const&) const@Base" 0.4.0+14.04.20140312.1
131 (c++)"unity::scopes::internal::RuntimeImpl::string_to_proxy(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0.4.0+14.04.20140312.1
132@@ -604,7 +611,9 @@
133 (c++)"unity::scopes::internal::ScopeLoader::name() const@Base" 0.4.0+14.04.20140312.1
134 (c++)"unity::scopes::internal::ScopeLoader::libpath() const@Base" 0.4.0+14.04.20140312.1
135 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::get_base_url(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0.4.0+14.04.20140312.1
136+ (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::get_metadata(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0replaceme
137 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::get_ssclient() const@Base" 0.4.0+14.04.20140312.1
138+ (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::list() const@Base" 0replaceme
139 (c++)"unity::scopes::internal::smartscopes::SSRegistryObject::has_scope(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0.4.0+14.04.20140312.1
140 (c++)"unity::scopes::internal::MiddlewareBase::runtime() const@Base" 0.4.0+14.04.20140312.1
141 (c++)"unity::scopes::internal::RegistryConfig::endpointdir() const@Base" 0.4.0+14.04.20140312.1
142@@ -617,6 +626,11 @@
143 (c++)"unity::scopes::internal::RegistryConfig::mw_kind() const@Base" 0.4.0+14.04.20140312.1
144 (c++)"unity::scopes::internal::RegistryConfig::endpoint() const@Base" 0.4.0+14.04.20140312.1
145 (c++)"unity::scopes::internal::RegistryConfig::identity() const@Base" 0.4.0+14.04.20140312.1
146+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::wait_for_state(unity::scopes::internal::RegistryObject::ScopeProcess::ProcessState, int) const@Base" 0replaceme
147+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::wait_for_state(std::unique_lock<std::mutex>&, unity::scopes::internal::RegistryObject::ScopeProcess::ProcessState, int) const@Base" 0replaceme
148+ (c++)"unity::scopes::internal::RegistryObject::ScopeProcess::state() const@Base" 0replaceme
149+ (c++)"unity::scopes::internal::RegistryObject::get_metadata(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0replaceme
150+ (c++)"unity::scopes::internal::RegistryObject::list() const@Base" 0replaceme
151 (c++)"unity::scopes::internal::MiddlewareFactory::find_unlocked(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0.4.0+14.04.20140312.1
152 (c++)"unity::scopes::internal::MiddlewareFactory::find(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0.4.0+14.04.20140312.1
153 (c++)"unity::scopes::internal::MiddlewareFactory::find(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0.4.0+14.04.20140312.1
154
155=== modified file 'demo/scopes/scope-A/scope-A.ini.in'
156--- demo/scopes/scope-A/scope-A.ini.in 2014-02-27 15:50:32 +0000
157+++ demo/scopes/scope-A/scope-A.ini.in 2014-03-28 15:41:24 +0000
158@@ -2,7 +2,7 @@
159 DisplayName = scope-A.DisplayName
160 Description = scope-A.Description
161 Art = scope-A.Art
162-Author = Canonical
163+Author = Canonical Ltd.
164 Icon = scope-A.Icon
165 SearchHint = scope-A.SearchHint
166 HotKey = scope-A.HotKey
167
168=== modified file 'demo/scopes/scope-B/scope-B.ini.in'
169--- demo/scopes/scope-B/scope-B.ini.in 2014-02-27 15:50:32 +0000
170+++ demo/scopes/scope-B/scope-B.ini.in 2014-03-28 15:41:24 +0000
171@@ -1,4 +1,4 @@
172 [ScopeConfig]
173 DisplayName = scope-B.DisplayName
174 Description = scope-B.Description
175-Author = Canonical
176+Author = Canonical Ltd.
177
178=== modified file 'demo/scopes/scope-C/scope-C.ini.in'
179--- demo/scopes/scope-C/scope-C.ini.in 2014-02-27 15:50:32 +0000
180+++ demo/scopes/scope-C/scope-C.ini.in 2014-03-28 15:41:24 +0000
181@@ -1,4 +1,4 @@
182 [ScopeConfig]
183 DisplayName = scope-C.DisplayName
184 Description = scope-C.Description
185-Author = Canonical
186+Author = Canonical Ltd.
187
188=== modified file 'demo/scopes/scope-D/scope-D.ini.in'
189--- demo/scopes/scope-D/scope-D.ini.in 2014-02-27 15:50:32 +0000
190+++ demo/scopes/scope-D/scope-D.ini.in 2014-03-28 15:41:24 +0000
191@@ -1,4 +1,4 @@
192 [ScopeConfig]
193 DisplayName = scope-D.DisplayName
194 Description = scope-D.Description
195-Author = Canonical
196+Author = Canonical Ltd.
197
198=== modified file 'demo/scopes/scope-N/scope-N.ini.in'
199--- demo/scopes/scope-N/scope-N.ini.in 2014-02-27 15:50:32 +0000
200+++ demo/scopes/scope-N/scope-N.ini.in 2014-03-28 15:41:24 +0000
201@@ -1,4 +1,4 @@
202 [ScopeConfig]
203 DisplayName = scope-N.DisplayName
204 Description = scope-N.Description
205-Author = Canonical
206+Author = Canonical Ltd.
207
208=== modified file 'demo/scopes/scope-S/scope-S.ini.in'
209--- demo/scopes/scope-S/scope-S.ini.in 2014-02-27 15:50:32 +0000
210+++ demo/scopes/scope-S/scope-S.ini.in 2014-03-28 15:41:24 +0000
211@@ -1,4 +1,4 @@
212 [ScopeConfig]
213 DisplayName = scope-S.DisplayName
214 Description = scope-S.Description
215-Author = Canonical
216+Author = Canonical Ltd.
217
218=== modified file 'doc/tutorial.dox'
219--- doc/tutorial.dox 2014-03-20 17:26:28 +0000
220+++ doc/tutorial.dox 2014-03-28 15:41:24 +0000
221@@ -33,7 +33,7 @@
222 be written with the intention of running remotely on the Smart Scopes Server.</b>
223
224 (For more information on how to deploy your scope to the Smart Scopes Server, or how to implement a native remote scope using
225-the SSS REST API see: <link_not_yet_available>)
226+the SSS REST API see: <i>link_not_yet_available</i>)
227
228 \section develop Developing scopes
229
230
231=== modified file 'include/unity/scopes/internal/RegistryObject.h'
232--- include/unity/scopes/internal/RegistryObject.h 2014-03-07 04:19:32 +0000
233+++ include/unity/scopes/internal/RegistryObject.h 2014-03-28 15:41:24 +0000
234@@ -13,16 +13,20 @@
235 * You should have received a copy of the GNU Lesser General Public License
236 * along with this program. If not, see <http://www.gnu.org/licenses/>.
237 *
238- * Authored by: Michi Henning <michi.henning@canonical.com>
239+ * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com>
240 */
241
242 #ifndef UNITY_SCOPES_INTERNAL_REGISTRYOBJECT_H
243 #define UNITY_SCOPES_INTERNAL_REGISTRYOBJECT_H
244
245+#include <core/posix/child_process.h>
246+
247 #include <unity/scopes/internal/MWRegistryProxyFwd.h>
248 #include <unity/scopes/internal/RegistryObjectBase.h>
249
250+#include <condition_variable>
251 #include <mutex>
252+#include <thread>
253
254 namespace unity
255 {
256@@ -33,40 +37,84 @@
257 namespace internal
258 {
259
260-class RegistryObjectPrivate;
261-
262-// Maintains a map of <scope name, scope proxy> pairs.
263-
264 class RegistryObject : public RegistryObjectBase
265 {
266 public:
267+ struct ScopeExecData
268+ {
269+ ScopeExecData() = default;
270+ ScopeExecData(std::initializer_list<std::string>) = delete;
271+ std::string scope_id;
272+ std::string scoperunner_path;
273+ std::string runtime_config;
274+ std::string scope_config;
275+ };
276+
277+public:
278 UNITY_DEFINES_PTRS(RegistryObject);
279
280- RegistryObject();
281+ RegistryObject(core::posix::ChildProcess::DeathObserver& death_observer);
282 virtual ~RegistryObject();
283
284 // Remote operation implementations
285- virtual ScopeMetadata get_metadata(std::string const& scope_id) override;
286- virtual MetadataMap list() override;
287+ virtual ScopeMetadata get_metadata(std::string const& scope_id) const override;
288+ virtual MetadataMap list() const override;
289 virtual ScopeProxy locate(std::string const& scope_id) override;
290
291 // Local methods
292 bool add_local_scope(std::string const& scope_id, ScopeMetadata const& scope,
293- std::vector<std::string> const& spawn_command);
294+ ScopeExecData const& scope_exec_data);
295 bool remove_local_scope(std::string const& scope_id);
296 void set_remote_registry(MWRegistryProxy const& remote_registry);
297
298-private:
299- void spawn_scope(std::string const& scope_id);
300- void shutdown();
301- static int kill_process(pid_t pid);
302- static bool is_dead(pid_t pid);
303-
304-private:
305+ bool is_scope_running( std::string const& scope_id );
306+
307+private:
308+ void on_process_death(core::posix::Process const& process);
309+
310+ class ScopeProcess
311+ {
312+ public:
313+ enum ProcessState
314+ {
315+ Stopped, Starting, Running, Stopping
316+ };
317+
318+ ScopeProcess(ScopeExecData exec_data);
319+ ScopeProcess(ScopeProcess const& other);
320+ ~ScopeProcess();
321+
322+ ProcessState state() const;
323+ bool wait_for_state(ProcessState state, int timeout_ms) const;
324+
325+ void exec(core::posix::ChildProcess::DeathObserver& death_observer);
326+ void kill();
327+
328+ bool on_process_death(pid_t pid);
329+
330+ private:
331+ // the following methods must be called with process_mutex_ locked
332+ void clear_handle_unlocked();
333+ void update_state_unlocked(ProcessState state);
334+
335+ bool wait_for_state(std::unique_lock<std::mutex>& lock,
336+ ProcessState state, int timeout_ms) const;
337+ void kill(std::unique_lock<std::mutex>& lock);
338+
339+ private:
340+ const ScopeExecData exec_data_;
341+ ProcessState state_ = Stopped;
342+ mutable std::mutex process_mutex_;
343+ mutable std::condition_variable state_change_cond_;
344+ core::posix::ChildProcess process_ = core::posix::ChildProcess::invalid();
345+ };
346+
347+private:
348+ core::posix::ChildProcess::DeathObserver& death_observer_;
349+ core::ScopedConnection death_observer_connection_;
350 mutable std::mutex mutex_;
351 MetadataMap scopes_;
352- std::map<std::string, pid_t> scope_processes_;
353- std::map<std::string, std::vector<std::string>> commands_;
354+ std::map<std::string, ScopeProcess> scope_processes_;
355 MWRegistryProxy remote_registry_;
356 };
357
358
359=== modified file 'include/unity/scopes/internal/RegistryObjectBase.h'
360--- include/unity/scopes/internal/RegistryObjectBase.h 2014-03-07 04:19:32 +0000
361+++ include/unity/scopes/internal/RegistryObjectBase.h 2014-03-28 15:41:24 +0000
362@@ -36,8 +36,8 @@
363 public:
364 UNITY_DEFINES_PTRS(RegistryObjectBase);
365
366- virtual ScopeMetadata get_metadata(std::string const& scope_id) = 0;
367- virtual MetadataMap list() = 0;
368+ virtual ScopeMetadata get_metadata(std::string const& scope_id) const = 0;
369+ virtual MetadataMap list() const = 0;
370 virtual ScopeProxy locate(std::string const& scope_id) = 0;
371 };
372
373
374=== modified file 'include/unity/scopes/internal/RuntimeImpl.h'
375--- include/unity/scopes/internal/RuntimeImpl.h 2014-03-07 04:19:32 +0000
376+++ include/unity/scopes/internal/RuntimeImpl.h 2014-03-28 15:41:24 +0000
377@@ -45,6 +45,7 @@
378 void destroy();
379
380 std::string scope_id() const;
381+ std::string configfile() const;
382 MiddlewareFactory const* factory() const;
383 RegistryProxy registry() const;
384 std::string registry_configfile() const;
385@@ -64,6 +65,7 @@
386
387 std::atomic_bool destroyed_;
388 std::string scope_id_;
389+ std::string configfile_;
390 MiddlewareFactory::UPtr middleware_factory_;
391 MiddlewareBase::SPtr middleware_;
392 mutable RegistryProxy registry_;
393
394=== modified file 'include/unity/scopes/internal/smartscopes/SSRegistryObject.h'
395--- include/unity/scopes/internal/smartscopes/SSRegistryObject.h 2014-03-20 16:19:04 +0000
396+++ include/unity/scopes/internal/smartscopes/SSRegistryObject.h 2014-03-28 15:41:24 +0000
397@@ -43,12 +43,12 @@
398 UNITY_DEFINES_PTRS(SSRegistryObject);
399
400 SSRegistryObject(MiddlewareBase::SPtr middleware, std::string const& ss_scope_endpoint,
401- uint no_reply_timeout, uint refresh_rate_in_sec, std::string const& sss_url = "",
402+ uint http_reply_timeout, uint refresh_rate_in_sec, std::string const& sss_url = "",
403 bool caching_enabled = true);
404 virtual ~SSRegistryObject() noexcept;
405
406- ScopeMetadata get_metadata(std::string const& scope_id) override;
407- MetadataMap list() override;
408+ ScopeMetadata get_metadata(std::string const& scope_id) const override;
409+ MetadataMap list() const override;
410
411 ScopeProxy locate(std::string const& scope_id) override;
412
413
414=== modified file 'scoperegistry/CMakeLists.txt'
415--- scoperegistry/CMakeLists.txt 2013-12-17 14:16:19 +0000
416+++ scoperegistry/CMakeLists.txt 2014-03-28 15:41:24 +0000
417@@ -1,7 +1,6 @@
418 set(SRC
419 FindFiles.cpp
420 scoperegistry.cpp
421- SignalThread.cpp
422 ScopeSet.cpp
423 )
424
425
426=== removed file 'scoperegistry/SignalThread.cpp'
427--- scoperegistry/SignalThread.cpp 2014-02-12 06:10:04 +0000
428+++ scoperegistry/SignalThread.cpp 1970-01-01 00:00:00 +0000
429@@ -1,121 +0,0 @@
430-/*
431- * Copyright (C) 2013 Canonical Ltd
432- *
433- * This program is free software: you can redistribute it and/or modify
434- * it under the terms of the GNU Lesser General Public License version 3 as
435- * published by the Free Software Foundation.
436- *
437- * This program is distributed in the hope that it will be useful,
438- * but WITHOUT ANY WARRANTY; without even the implied warranty of
439- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
440- * GNU Lesser General Public License for more details.
441- *
442- * You should have received a copy of the GNU Lesser General Public License
443- * along with this program. If not, see <http://www.gnu.org/licenses/>.
444- *
445- * Authored by: Michi Henning <michi.henning@canonical.com>
446- */
447-
448-#include <SignalThread.h>
449-
450-#include <unity/UnityExceptions.h>
451-
452-#include <cassert>
453-#include <iostream>
454-
455-#include <signal.h>
456-#include <string.h>
457-#include <unistd.h>
458-
459-using namespace std;
460-using namespace unity;
461-
462-namespace scoperegistry
463-{
464-
465-SignalThread::SignalThread() :
466- done_(false)
467-{
468- // Block signals in the caller, so they are delivered to the thread we are about to create.
469- sigemptyset(&sigs_);
470- sigaddset(&sigs_, SIGINT);
471- sigaddset(&sigs_, SIGHUP);
472- sigaddset(&sigs_, SIGTERM);
473- sigaddset(&sigs_, SIGUSR1);
474- int err = pthread_sigmask(SIG_BLOCK, &sigs_, nullptr);
475- if (err != 0)
476- {
477- throw SyscallException("pthread_sigmask failed", err);
478- }
479-
480- // Make ourselves a process group leader.
481- setpgid(0, 0);
482-
483- // Run a signal handling thread that waits for any of the above signals.
484- lock_guard<mutex> lock(mutex_);
485- handler_thread_ = thread([this]{ this->wait_for_sigs(); });
486-}
487-
488-SignalThread::~SignalThread()
489-{
490- stop();
491- handler_thread_.join();
492-}
493-
494-void SignalThread::activate(function<void()> callback)
495-{
496- lock_guard<mutex> lock(mutex_);
497- callback_ = callback;
498-}
499-
500-void SignalThread::stop()
501-{
502- lock_guard<mutex> lock(mutex_);
503- if (!done_)
504- {
505- done_ = true;
506- kill(getpid(), SIGUSR1);
507- }
508-}
509-
510-// Wait for termination signals. When a termination signal arrives, we
511-// invoke the callback (if set). If we receive SIGUSR1 (because
512-// we are terminating ourself, we exit the thread.
513-
514-void SignalThread::wait_for_sigs()
515-{
516- int signo;
517- for (;;)
518- {
519- int err = sigwait(&sigs_, &signo);
520- if (err != 0)
521- {
522- cerr << "scoperegistry: sigwait failed: " << strerror(err) << endl;
523- _exit(1);
524- }
525- switch (signo)
526- {
527- case SIGINT:
528- case SIGHUP:
529- case SIGTERM:
530- {
531- lock_guard<mutex> lock(mutex_);
532- if (callback_)
533- {
534- callback_();
535- }
536- break;
537- }
538- case SIGUSR1:
539- {
540- return;
541- }
542- default:
543- {
544- assert(false);
545- }
546- }
547- }
548-}
549-
550-} // namespace scoperegistry
551
552=== removed file 'scoperegistry/SignalThread.h'
553--- scoperegistry/SignalThread.h 2014-02-12 06:10:04 +0000
554+++ scoperegistry/SignalThread.h 1970-01-01 00:00:00 +0000
555@@ -1,55 +0,0 @@
556-/*
557- * Copyright (C) 2013 Canonical Ltd
558- *
559- * This program is free software: you can redistribute it and/or modify
560- * it under the terms of the GNU Lesser General Public License version 3 as
561- * published by the Free Software Foundation.
562- *
563- * This program is distributed in the hope that it will be useful,
564- * but WITHOUT ANY WARRANTY; without even the implied warranty of
565- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
566- * GNU Lesser General Public License for more details.
567- *
568- * You should have received a copy of the GNU Lesser General Public License
569- * along with this program. If not, see <http://www.gnu.org/licenses/>.
570- *
571- * Authored by: Michi Henning <michi.henning@canonical.com>
572- */
573-
574-#ifndef SCOPEREGISTRY_SIGNAL_THREAD_H
575-#define SCOPEREGISTRY_SIGNAL_THREAD_H
576-
577-#include <unity/util/NonCopyable.h>
578-
579-#include <mutex>
580-#include <thread>
581-
582-#include <sys/types.h>
583-
584-namespace scoperegistry
585-{
586-
587-class SignalThread final
588-{
589-public:
590- NONCOPYABLE(SignalThread);
591-
592- SignalThread();
593- ~SignalThread();
594-
595- void activate(std::function<void()> callback);
596- void stop();
597-
598-private:
599- void wait_for_sigs();
600-
601- std::mutex mutex_;
602- std::function<void()> callback_;
603- bool done_;
604- std::thread handler_thread_;
605- sigset_t sigs_;
606-};
607-
608-} // namespace scoperegistry
609-
610-#endif
611
612=== modified file 'scoperegistry/scoperegistry.cpp'
613--- scoperegistry/scoperegistry.cpp 2014-03-13 12:33:32 +0000
614+++ scoperegistry/scoperegistry.cpp 2014-03-28 15:41:24 +0000
615@@ -17,7 +17,6 @@
616 */
617
618 #include "FindFiles.h"
619-#include "SignalThread.h"
620
621 #include <unity/scopes/internal/MiddlewareFactory.h>
622 #include <unity/scopes/internal/MWRegistry.h>
623@@ -74,7 +73,7 @@
624
625 // Return a map of <scope, config_file> pairs for all scopes (Canonical and OEM scopes).
626 // If a Canonical scope is overrideable and the OEM has configured a scope with the
627-// same name, the OEM scope overrides the Canonical one.
628+// same id, the OEM scope overrides the Canonical one.
629
630 map<string, string> find_local_scopes(string const& scope_installdir, string const& oem_installdir)
631 {
632@@ -90,22 +89,22 @@
633 for (auto&& path : config_files)
634 {
635 string file_name = basename(const_cast<char*>(string(path).c_str())); // basename() modifies its argument
636- string scope_name = strip_suffix(file_name, ".ini");
637+ string scope_id = strip_suffix(file_name, ".ini");
638 try
639 {
640 ScopeConfig config(path);
641 if (config.overrideable())
642 {
643- overrideable_scopes[scope_name] = path;
644+ overrideable_scopes[scope_id] = path;
645 }
646 else
647 {
648- fixed_scopes[scope_name] = path;
649+ fixed_scopes[scope_id] = path;
650 }
651 }
652 catch (unity::Exception const& e)
653 {
654- error("ignoring scope \"" + scope_name + "\": configuration error:\n" + e.what());
655+ error("ignoring scope \"" + scope_id + "\": configuration error:\n" + e.what());
656 }
657 }
658
659@@ -117,10 +116,10 @@
660 for (auto&& path : oem_paths)
661 {
662 string file_name = basename(const_cast<char*>(string(path).c_str())); // basename() modifies its argument
663- string scope_name = strip_suffix(file_name, ".ini");
664- if (fixed_scopes.find(scope_name) == fixed_scopes.end())
665+ string scope_id = strip_suffix(file_name, ".ini");
666+ if (fixed_scopes.find(scope_id) == fixed_scopes.end())
667 {
668- overrideable_scopes[scope_name] = path; // Replaces scope if it was present already
669+ overrideable_scopes[scope_id] = path; // Replaces scope if it was present already
670 }
671 else
672 {
673@@ -189,14 +188,18 @@
674 catch (NotFoundException const&)
675 {
676 }
677+
678 ScopeProxy proxy = ScopeImpl::create(mw->create_scope_proxy(pair.first), mw->runtime(), pair.first);
679 mi->set_proxy(proxy);
680 auto meta = ScopeMetadataImpl::create(move(mi));
681- vector<string> spawn_command;
682- spawn_command.push_back(scoperunner_path);
683- spawn_command.push_back(config_file);
684- spawn_command.push_back(pair.second);
685- registry->add_local_scope(pair.first, move(meta), spawn_command);
686+
687+ RegistryObject::ScopeExecData exec_data;
688+ exec_data.scope_id = pair.first;
689+ exec_data.scoperunner_path = scoperunner_path;
690+ exec_data.runtime_config = config_file;
691+ exec_data.scope_config = pair.second;
692+
693+ registry->add_local_scope(pair.first, move(meta), exec_data);
694 }
695 catch (unity::Exception const& e)
696 {
697@@ -244,10 +247,33 @@
698 char const* const config_file = argc > 1 ? argv[1] : "";
699 int exit_status = 1;
700
701- SignalThread signal_thread;
702-
703 try
704 {
705+ // We shutdown the runtime whenever these signals happen.
706+ auto termination_trap = core::posix::trap_signals_for_all_subsequent_threads(
707+ {
708+ core::posix::Signal::sig_int,
709+ core::posix::Signal::sig_hup,
710+ core::posix::Signal::sig_term
711+ });
712+
713+ // And we maintain our list of processes with the help of sig_chld.
714+ auto child_trap = core::posix::trap_signals_for_all_subsequent_threads(
715+ {
716+ core::posix::Signal::sig_chld
717+ });
718+
719+ // The death observer is required to make sure that we reap all child processes
720+ // whenever multiple sigchld's are compressed together.
721+ auto death_observer =
722+ core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(
723+ child_trap);
724+
725+ // Starting up both traps.
726+ std::thread termination_trap_worker([termination_trap]() { termination_trap->run(); });
727+ std::thread child_trap_worker([child_trap]() { child_trap->run(); });
728+
729+ // And finally creating our runtime.
730 RuntimeImpl::UPtr runtime = RuntimeImpl::create("Registry", config_file);
731
732 string identity = runtime->registry_identity();
733@@ -278,10 +304,22 @@
734
735 // Inform the signal thread that it should shutdown the middleware
736 // if we get a termination signal.
737- signal_thread.activate([middleware]{ middleware->stop(); });
738+ termination_trap->signal_raised().connect([middleware](core::posix::Signal signal)
739+ {
740+ switch(signal)
741+ {
742+ case core::posix::Signal::sig_int:
743+ case core::posix::Signal::sig_hup:
744+ case core::posix::Signal::sig_term:
745+ middleware->stop();
746+ break;
747+ default:
748+ break;
749+ }
750+ });
751
752 // The registry object stores the local and remote scopes
753- RegistryObject::SPtr registry(new RegistryObject);
754+ RegistryObject::SPtr registry(new RegistryObject(*death_observer));
755
756 // Add the metadata for each scope to the lookup table.
757 // We do this before starting any of the scopes, so aggregating scopes don't get a lookup failure if
758@@ -295,11 +333,11 @@
759 for (auto i = 2; i < argc; ++i)
760 {
761 string file_name = basename(const_cast<char*>(string(argv[i]).c_str())); // basename() modifies its argument
762- string scope_name = strip_suffix(file_name, ".ini");
763- local_scopes[scope_name] = argv[i]; // operator[] overwrites pre-existing entries
764+ string scope_id = strip_suffix(file_name, ".ini");
765+ local_scopes[scope_id] = argv[i]; // operator[] overwrites pre-existing entries
766 }
767
768- add_local_scopes(registry, local_scopes, middleware, scoperunner_path, config_file);
769+ add_local_scopes(registry, local_scopes, middleware, scoperunner_path, runtime->configfile());
770 if (ss_reg_id.empty() || ss_reg_endpoint.empty())
771 {
772 error("no remote registry configured, only local scopes will be available");
773@@ -342,6 +380,19 @@
774
775 // Wait until we are done, which happens if we receive a termination signal.
776 middleware->wait_for_shutdown();
777+
778+ // Stop termination_trap
779+ termination_trap->stop();
780+ if (termination_trap_worker.joinable())
781+ termination_trap_worker.join();
782+
783+ // Please note that the child_trap must only be stopped once the
784+ // termination_trap thread has been joined. We otherwise will encounter
785+ // a race between the middleware shutting down and not receiving sigchld anymore.
786+ child_trap->stop();
787+ if (child_trap_worker.joinable())
788+ child_trap_worker.join();
789+
790 exit_status = 0;
791 }
792 catch (std::exception const& e)
793
794=== modified file 'scoperunner/scoperunner.cpp'
795--- scoperunner/scoperunner.cpp 2014-01-24 03:18:25 +0000
796+++ scoperunner/scoperunner.cpp 2014-03-28 15:41:24 +0000
797@@ -24,6 +24,8 @@
798 #include <unity/scopes/ScopeExceptions.h>
799 #include <unity/UnityExceptions.h>
800
801+#include <core/posix/signal.h>
802+
803 #include <cassert>
804 #include <future>
805 #include <iostream>
806@@ -92,7 +94,8 @@
807
808 // Scope thread start function.
809
810-void scope_thread(string const& runtime_config,
811+void scope_thread(std::shared_ptr<core::posix::SignalTrap> trap,
812+ string const& runtime_config,
813 string const& scope_name,
814 string const& lib_dir,
815 promise<void> finished_promise)
816@@ -114,6 +117,12 @@
817 auto scope = unique_ptr<ScopeObject>(new ScopeObject(rt.get(), loader->scope_base()));
818 auto proxy = mw->add_scope_object(scope_name, move(scope));
819
820+ trap->signal_raised().connect([loader, mw](core::posix::Signal)
821+ {
822+ loader->stop();
823+ mw->stop();
824+ });
825+
826 mw->wait_for_shutdown();
827
828 // Collect exit status from the run thread. If this throws, the ScopeLoader
829@@ -135,6 +144,14 @@
830
831 int run_scopes(string const& runtime_config, vector<string> config_files)
832 {
833+ auto trap = core::posix::trap_signals_for_all_subsequent_threads(
834+ {
835+ core::posix::Signal::sig_hup,
836+ core::posix::Signal::sig_term
837+ });
838+
839+ std::thread trap_worker([trap]() { trap->run(); });
840+
841 for (auto file : config_files)
842 {
843 string file_name = basename(const_cast<char*>(string(file).c_str())); // basename() modifies its argument
844@@ -152,7 +169,7 @@
845 // We collect exit status from the thread via the future from each promise.
846 promise<void> p;
847 auto f = p.get_future();
848- thread t(scope_thread, runtime_config, scope_name, dir, move(p));
849+ thread t(scope_thread, trap, runtime_config, scope_name, dir, move(p));
850 auto id = t.get_id();
851 threads[id] = ThreadFuture { move(t), move(f) };
852 }
853@@ -180,6 +197,12 @@
854 ++num_errors;
855 }
856 }
857+
858+ trap->stop();
859+
860+ if (trap_worker.joinable())
861+ trap_worker.join();
862+
863 return num_errors;
864 }
865
866@@ -188,6 +211,13 @@
867 int
868 main(int argc, char* argv[])
869 {
870+ // sig masks are inherited by child processes when forked.
871+ // we do not want to inherit our parent's (scoperegistry)
872+ // sig mask, hence we clear it immediately on entry.
873+ sigset_t set;
874+ ::sigemptyset(&set);
875+ ::pthread_sigmask(SIG_SETMASK, &set, nullptr);
876+
877 prog_name = basename(argv[0]);
878 if (argc < 3)
879 {
880
881=== modified file 'smartscopesproxy/smartscopesproxy.cpp'
882--- smartscopesproxy/smartscopesproxy.cpp 2014-02-20 19:19:25 +0000
883+++ smartscopesproxy/smartscopesproxy.cpp 2014-03-28 15:41:24 +0000
884@@ -23,6 +23,8 @@
885
886 #include <unity/scopes/internal/DfltConfig.h>
887
888+#include <core/posix/signal.h>
889+
890 #include <cassert>
891 #include <signal.h>
892 #include <libgen.h>
893@@ -74,10 +76,17 @@
894
895 try
896 {
897+ auto trap = core::posix::trap_signals_for_all_subsequent_threads(
898+ {
899+ core::posix::Signal::sig_int,
900+ core::posix::Signal::sig_hup,
901+ core::posix::Signal::sig_term
902+ });
903+
904 ///! TODO: get these from config
905 std::string ss_reg_id = "SSRegistry";
906 std::string ss_scope_id = "SmartScope";
907- uint const no_reply_timeout = 20000;
908+ uint const http_reply_timeout = 20000;
909 uint const ss_reg_refresh_rate = 60 * 60 * 24; // 24 hour refresh (in seconds)
910
911 // Instantiate SS registry and scopes runtimes
912@@ -93,9 +102,17 @@
913 MiddlewareBase::SPtr reg_mw = reg_rt->factory()->find(ss_reg_id, mw_kind);
914 MiddlewareBase::SPtr scope_mw = scope_rt->factory()->create(ss_scope_id, mw_kind, mw_configfile);
915
916+ trap->signal_raised().connect([reg_mw, scope_mw](core::posix::Signal)
917+ {
918+ scope_mw->stop();
919+ reg_mw->stop();
920+ });
921+
922+ std::thread trap_worker([trap]() { trap->run(); });
923+
924 // Instantiate a SS registry object
925 SSRegistryObject::SPtr reg(new SSRegistryObject(reg_mw, scope_mw->get_scope_endpoint(),
926- no_reply_timeout, ss_reg_refresh_rate));
927+ http_reply_timeout, ss_reg_refresh_rate));
928
929 // Instantiate a SS scope object
930 SSScopeObject::UPtr scope(new SSScopeObject(ss_scope_id, scope_mw, reg));
931@@ -114,6 +131,11 @@
932 scope_mw->wait_for_shutdown();
933 reg_mw->wait_for_shutdown();
934
935+ trap->stop();
936+
937+ if (trap_worker.joinable())
938+ trap_worker.join();
939+
940 exit_status = 0;
941 }
942 catch (std::exception const& e)
943
944=== modified file 'src/scopes/ScopeMetadata.cpp'
945--- src/scopes/ScopeMetadata.cpp 2014-03-13 12:33:32 +0000
946+++ src/scopes/ScopeMetadata.cpp 2014-03-28 15:41:24 +0000
947@@ -42,6 +42,7 @@
948 assert(p->proxy());
949 assert(!p->display_name().empty());
950 assert(!p->description().empty());
951+ assert(!p->author().empty());
952 }
953
954 ScopeMetadata::ScopeMetadata(ScopeMetadata const& other) :
955
956=== modified file 'src/scopes/internal/RegistryObject.cpp'
957--- src/scopes/internal/RegistryObject.cpp 2014-03-07 12:31:25 +0000
958+++ src/scopes/internal/RegistryObject.cpp 2014-03-28 15:41:24 +0000
959@@ -13,20 +13,18 @@
960 * You should have received a copy of the GNU Lesser General Public License
961 * along with this program. If not, see <http://www.gnu.org/licenses/>.
962 *
963- * Authored by: Michi Henning <michi.henning@canonical.com>
964+ * Authored by: Marcus Tomlinson <marcus.tomlinson@canonical.com>
965 */
966
967 #include <unity/scopes/internal/RegistryObject.h>
968
969+#include <core/posix/child_process.h>
970+#include <core/posix/exec.h>
971+
972 #include <unity/scopes/internal/MWRegistry.h>
973 #include <unity/scopes/ScopeExceptions.h>
974 #include <unity/UnityExceptions.h>
975
976-#include <signal.h>
977-#include <cassert>
978-#include <sys/wait.h>
979-#include <unistd.h>
980-
981 using namespace std;
982
983 namespace unity
984@@ -38,43 +36,58 @@
985 namespace internal
986 {
987
988-RegistryObject::RegistryObject()
989+///! TODO: get from config
990+static const int c_process_wait_timeout = 1000;
991+
992+RegistryObject::RegistryObject(core::posix::ChildProcess::DeathObserver& death_observer)
993+ : death_observer_(death_observer),
994+ death_observer_connection_
995+ {
996+ death_observer_.child_died().connect([this](const core::posix::ChildProcess& cp)
997+ {
998+ on_process_death(cp);
999+ })
1000+ }
1001+
1002 {
1003 }
1004
1005 RegistryObject::~RegistryObject()
1006 {
1007- try
1008- {
1009- shutdown();
1010- }
1011- catch (std::exception const& e)
1012- {
1013- fprintf(stderr, "scoperegistry: shutdown error: %s\n", e.what());
1014- }
1015- catch (...)
1016- {
1017- fprintf(stderr, "scoperegistry: unknown exception during shutdown\n");
1018- }
1019+ // kill all scope processes
1020+ for (auto& scope_process : scope_processes_)
1021+ {
1022+ try
1023+ {
1024+ scope_process.second.kill();
1025+ }
1026+ catch(std::exception const& e)
1027+ {
1028+ cerr << "RegistryObject::~RegistryObject(): " << e.what() << endl;
1029+ }
1030+ }
1031+
1032+ // clear scope maps
1033+ scopes_.clear();
1034+ scope_processes_.clear();
1035 }
1036
1037-ScopeMetadata RegistryObject::get_metadata(std::string const& scope_id)
1038+ScopeMetadata RegistryObject::get_metadata(std::string const& scope_id) const
1039 {
1040 lock_guard<decltype(mutex_)> lock(mutex_);
1041 // If the id is empty, it was sent as empty by the remote client.
1042 if (scope_id.empty())
1043 {
1044- throw unity::InvalidArgumentException("RegistryObject::get_metadata(): Cannot search for scope with empty name");
1045+ throw unity::InvalidArgumentException("RegistryObject::get_metadata(): Cannot search for scope with empty id");
1046 }
1047
1048 // Look for the scope in both the local and the remote map.
1049 // Local scopes take precedence over remote ones of the same
1050- // name. (Ideally, this will never happen, except maybe
1051- // during development.)
1052- auto const& it = scopes_.find(scope_id);
1053- if (it != scopes_.end())
1054+ // id. (Ideally, this should never happen.)
1055+ auto const& scope_it = scopes_.find(scope_id);
1056+ if (scope_it != scopes_.end())
1057 {
1058- return it->second;
1059+ return scope_it->second;
1060 }
1061
1062 if (remote_registry_)
1063@@ -85,14 +98,14 @@
1064 throw NotFoundException("RegistryObject::get_metadata(): no such scope: ", scope_id);
1065 }
1066
1067-MetadataMap RegistryObject::list()
1068+MetadataMap RegistryObject::list() const
1069 {
1070 lock_guard<decltype(mutex_)> lock(mutex_);
1071 MetadataMap all_scopes(scopes_); // Local scopes
1072
1073- // If a remote scope has the same name as a local one,
1074+ // If a remote scope has the same id as a local one,
1075 // this will not overwrite a local scope with a remote
1076- // one if they have the same name.
1077+ // one if they have the same id.
1078 if (remote_registry_)
1079 {
1080 MetadataMap remote_scopes = remote_registry_->list();
1081@@ -102,34 +115,58 @@
1082 return all_scopes;
1083 }
1084
1085+ScopeProxy RegistryObject::locate(std::string const& scope_id)
1086+{
1087+ decltype(scopes_.cbegin()) scope_it;
1088+ decltype(scope_processes_.begin()) proc_it;
1089+
1090+ {
1091+ lock_guard<decltype(mutex_)> lock(mutex_);
1092+ // If the id is empty, it was sent as empty by the remote client.
1093+ if (scope_id.empty())
1094+ {
1095+ throw unity::InvalidArgumentException("Registry::locate(): Cannot locate scope with empty id");
1096+ }
1097+
1098+ scope_it = scopes_.find(scope_id);
1099+ if (scope_it == scopes_.end())
1100+ {
1101+ throw NotFoundException("Registry::locate(): Tried to locate unknown local scope", scope_id);
1102+ }
1103+
1104+ proc_it = scope_processes_.find(scope_id);
1105+ if (proc_it == scope_processes_.end())
1106+ {
1107+ throw NotFoundException("Registry::locate(): Tried to exec unknown local scope", scope_id);
1108+ }
1109+ }
1110+
1111+ proc_it->second.exec(death_observer_);
1112+ return scope_it->second.proxy();
1113+}
1114+
1115 bool RegistryObject::add_local_scope(std::string const& scope_id, ScopeMetadata const& metadata,
1116- std::vector<std::string> const& spawn_command)
1117+ ScopeExecData const& exec_data)
1118 {
1119 lock_guard<decltype(mutex_)> lock(mutex_);
1120 bool return_value = true;
1121 if (scope_id.empty())
1122 {
1123- throw unity::InvalidArgumentException("RegistryObject::add_local_scope(): Cannot add scope with empty name");
1124+ throw unity::InvalidArgumentException("RegistryObject::add_local_scope(): Cannot add scope with empty id");
1125 }
1126- if(scope_id.find('/') != std::string::npos) {
1127- throw unity::InvalidArgumentException("RegistryObject::add_local_scope(): Cannot create a scope with "
1128- "a slash in its name: " + scope_id);
1129+ if (scope_id.find('/') != std::string::npos)
1130+ {
1131+ throw unity::InvalidArgumentException("RegistryObject::add_local_scope(): Cannot create a scope with '/' in its id");
1132 }
1133
1134 if (scopes_.find(scope_id) != scopes_.end())
1135 {
1136- auto proc = scope_processes_.find(scope_id);
1137- if (proc != scope_processes_.end())
1138- {
1139- kill_process(proc->second);
1140- scope_processes_.erase(scope_id);
1141- }
1142 scopes_.erase(scope_id);
1143- commands_.erase(scope_id);
1144+ scope_processes_.erase(scope_id);
1145 return_value = false;
1146 }
1147 scopes_.insert(make_pair(scope_id, metadata));
1148- commands_[scope_id] = spawn_command;
1149+ scope_processes_.insert(make_pair(scope_id, ScopeProcess(exec_data)));
1150 return return_value;
1151 }
1152
1153@@ -140,10 +177,10 @@
1154 if (scope_id.empty())
1155 {
1156 throw unity::InvalidArgumentException("RegistryObject::remove_local_scope(): Cannot remove scope "
1157- "with empty name");
1158+ "with empty id");
1159 }
1160
1161- commands_.erase(scope_id);
1162+ scope_processes_.erase(scope_id);
1163 return scopes_.erase(scope_id) == 1;
1164 }
1165
1166@@ -153,96 +190,218 @@
1167 remote_registry_ = remote_registry;
1168 }
1169
1170-ScopeProxy RegistryObject::locate(std::string const& scope_id)
1171-{
1172- lock_guard<decltype(mutex_)> lock(mutex_);
1173- // If the id is empty, it was sent as empty by the remote client.
1174- if (scope_id.empty())
1175- throw unity::InvalidArgumentException("RegistryObject::locate(): Cannot locate scope with empty name");
1176- auto metadata = scopes_.find(scope_id);
1177- if (metadata == scopes_.end())
1178- {
1179- throw NotFoundException("RegistryObject::locate(): Tried to obtain unknown scope: ", scope_id);
1180- }
1181- auto search = scope_processes_.find(scope_id);
1182- if (search == scope_processes_.end() || is_dead(search->second))
1183- {
1184- spawn_scope(scope_id);
1185- }
1186- return metadata->second.proxy();
1187-}
1188-
1189-void RegistryObject::spawn_scope(std::string const& scope_id)
1190-{
1191- if (scopes_.find(scope_id) == scopes_.end())
1192- {
1193- throw NotFoundException("RegistryObject::spawn_scope(): Tried to spawn an unknown scope: ", scope_id);
1194- }
1195- auto process = scope_processes_.find(scope_id);
1196- if (process != scope_processes_.end())
1197- {
1198- assert(is_dead(process->second));
1199- int status;
1200- waitpid(process->second, &status, 0);
1201- if (status != 0)
1202- {
1203- printf("scope %s has exited with nonzero error status %d.\n", scope_id.c_str(), status);
1204- }
1205- scope_processes_.erase(scope_id);
1206- }
1207-
1208- pid_t pid;
1209- switch (pid = fork())
1210- {
1211- case -1:
1212- {
1213- throw SyscallException("RegistryObject::spawn_scope(): cannot fork", errno);
1214- }
1215- case 0: // child
1216- {
1217- const vector<string>& cmd = commands_[scope_id];
1218- assert(cmd.size() == 3);
1219- // Includes room for final NULL element.
1220- unique_ptr<char const* []> argv(new char const*[4]);
1221- argv[0] = cmd[0].c_str();
1222- argv[1] = cmd[1].c_str();
1223- argv[2] = cmd[2].c_str();
1224- argv[3] = nullptr;
1225- execv(argv[0], const_cast<char* const*>(argv.get()));
1226- throw SyscallException("REgistryObject::spawn_scope(): cannot exec scoperunner", errno);
1227- }
1228- }
1229- const vector<string>& cmd = commands_[scope_id];
1230- printf("spawning scope %s to process number %d with command line %s %s %s.\n",
1231- scope_id.c_str(), (int)pid, cmd[0].c_str(), cmd[1].c_str(), cmd[2].c_str());
1232- scope_processes_[scope_id] = pid;
1233-}
1234-
1235-void RegistryObject::shutdown()
1236-{
1237- for (const auto &i : scope_processes_)
1238- {
1239- kill_process(i.second);
1240- // If and when we move to graceful shutdown, check that exit status
1241- // was zero and print error message here.
1242- }
1243- scope_processes_.clear();
1244- commands_.clear();
1245-}
1246-
1247-int RegistryObject::kill_process(pid_t pid) {
1248- int exitcode;
1249- // Currently just shoot children dead.
1250- // If we want to get fancy and give them a graceful
1251- // warning, this is the place to do it.
1252- kill(pid, SIGKILL);
1253- waitpid(pid, &exitcode, 0);
1254- return exitcode;
1255-}
1256-
1257-bool RegistryObject::is_dead(pid_t pid)
1258-{
1259- return kill(pid, 0) < 0 && errno == ESRCH;
1260+bool RegistryObject::is_scope_running( std::string const& scope_id )
1261+{
1262+ auto it = scope_processes_.find( scope_id );
1263+ if (it != scope_processes_.end())
1264+ {
1265+ return it->second.state() != ScopeProcess::ProcessState::Stopped;
1266+ }
1267+
1268+ throw NotFoundException("RegistryObject::is_scope_process_running(): no such scope: ", scope_id);
1269+}
1270+
1271+void RegistryObject::on_process_death(core::posix::Process const& process)
1272+{
1273+ // the death observer has signaled that a child has died.
1274+ // broadcast this message to each scope process until we have found the process in question.
1275+ // (this is slightly more efficient than just connecting the signal to every scope process)
1276+ pid_t pid = process.pid();
1277+ for (auto& scope_process : scope_processes_)
1278+ {
1279+ if (scope_process.second.on_process_death(pid))
1280+ break;
1281+ }
1282+}
1283+
1284+RegistryObject::ScopeProcess::ScopeProcess(ScopeExecData exec_data)
1285+ : exec_data_(exec_data)
1286+{
1287+}
1288+
1289+RegistryObject::ScopeProcess::ScopeProcess(ScopeProcess const& other)
1290+ : exec_data_(other.exec_data_)
1291+{
1292+}
1293+
1294+RegistryObject::ScopeProcess::~ScopeProcess()
1295+{
1296+ try
1297+ {
1298+ kill();
1299+ }
1300+ catch(std::exception const& e)
1301+ {
1302+ cerr << "RegistryObject::ScopeProcess::~ScopeProcess(): " << e.what() << endl;
1303+ }
1304+}
1305+
1306+RegistryObject::ScopeProcess::ProcessState RegistryObject::ScopeProcess::state() const
1307+{
1308+ std::lock_guard<std::mutex> lock(process_mutex_);
1309+ return state_;
1310+}
1311+
1312+bool RegistryObject::ScopeProcess::wait_for_state(ProcessState state, int timeout_ms) const
1313+{
1314+ std::unique_lock<std::mutex> lock(process_mutex_);
1315+ return wait_for_state(lock, state, timeout_ms);
1316+}
1317+
1318+void RegistryObject::ScopeProcess::exec(core::posix::ChildProcess::DeathObserver& death_observer)
1319+{
1320+ std::unique_lock<std::mutex> lock(process_mutex_);
1321+
1322+ // 1. check if the scope is running.
1323+ // 1.1. if scope already running, return.
1324+ if (state_ == ScopeProcess::Running)
1325+ {
1326+ return;
1327+ }
1328+ // 1.2. if scope running but is “stopping”, wait for it to stop / kill it.
1329+ else if (state_ == ScopeProcess::Stopping)
1330+ {
1331+ if (!wait_for_state(lock, ScopeProcess::Stopped, c_process_wait_timeout))
1332+ {
1333+ cerr << "RegistryObject::ScopeProcess::exec(): Force killing process. Scope: \""
1334+ << exec_data_.scope_id << "\" took too long to stop." << endl;
1335+ try
1336+ {
1337+ kill(lock);
1338+ }
1339+ catch(std::exception const& e)
1340+ {
1341+ cerr << "RegistryObject::ScopeProcess::exec(): " << e.what() << endl;
1342+ }
1343+ }
1344+ }
1345+
1346+ // 2. exec the scope.
1347+ update_state_unlocked(Starting);
1348+
1349+ const std::string program{exec_data_.scoperunner_path};
1350+ const std::vector<std::string> argv = {exec_data_.runtime_config, exec_data_.scope_config};
1351+
1352+ std::map<std::string, std::string> env;
1353+ core::posix::this_process::env::for_each([&env](const std::string& key, const std::string& value)
1354+ {
1355+ env.insert(std::make_pair(key, value));
1356+ });
1357+
1358+ {
1359+ process_ = core::posix::exec(program, argv, env,
1360+ core::posix::StandardStream::stdin | core::posix::StandardStream::stdout);
1361+ if (process_.pid() <= 0)
1362+ {
1363+ clear_handle_unlocked();
1364+ throw unity::ResourceException("RegistryObject::ScopeProcess::exec(): Failed to exec scope via command: \""
1365+ + exec_data_.scoperunner_path + " " + exec_data_.runtime_config + " "
1366+ + exec_data_.scope_config + "\"");
1367+ }
1368+ }
1369+
1370+ ///! TODO: This should not be here. A ready signal from the scope should trigger "running".
1371+ update_state_unlocked(Running);
1372+
1373+ // 3. wait for scope to be "running".
1374+ // 3.1. when ready, return.
1375+ // 3.2. OR if timeout, kill process and throw.
1376+ if (!wait_for_state(lock, ScopeProcess::Running, c_process_wait_timeout))
1377+ {
1378+ try
1379+ {
1380+ kill(lock);
1381+ }
1382+ catch(std::exception const& e)
1383+ {
1384+ cerr << "RegistryObject::ScopeProcess::exec(): " << e.what() << endl;
1385+ }
1386+ throw unity::ResourceException("RegistryObject::ScopeProcess::exec(): exec aborted. Scope: \""
1387+ + exec_data_.scope_id + "\" took too long to start.");
1388+ }
1389+
1390+ cout << "RegistryObject::ScopeProcess::exec(): Process for scope: \"" << exec_data_.scope_id << "\" started" << endl;
1391+
1392+ // 4. add the scope process to the death observer
1393+ death_observer.add(process_);
1394+}
1395+
1396+void RegistryObject::ScopeProcess::kill()
1397+{
1398+ std::unique_lock<std::mutex> lock(process_mutex_);
1399+ kill(lock);
1400+}
1401+
1402+bool RegistryObject::ScopeProcess::on_process_death(pid_t pid)
1403+{
1404+ std::lock_guard<std::mutex> lock(process_mutex_);
1405+
1406+ // check if this is the process reported to have died
1407+ if (pid == process_.pid())
1408+ {
1409+ cout << "RegistryObject::ScopeProcess::on_process_death(): Process for scope: \"" << exec_data_.scope_id
1410+ << "\" terminated" << endl;
1411+ clear_handle_unlocked();
1412+ return true;
1413+ }
1414+
1415+ return false;
1416+}
1417+
1418+void RegistryObject::ScopeProcess::clear_handle_unlocked()
1419+{
1420+ process_ = core::posix::ChildProcess::invalid();
1421+ update_state_unlocked(Stopped);
1422+}
1423+
1424+void RegistryObject::ScopeProcess::update_state_unlocked(ProcessState state)
1425+{
1426+ state_ = state;
1427+ state_change_cond_.notify_all();
1428+}
1429+
1430+bool RegistryObject::ScopeProcess::wait_for_state(std::unique_lock<std::mutex>& lock,
1431+ ProcessState state, int timeout_ms) const
1432+{
1433+ return state_change_cond_.wait_for(lock,
1434+ std::chrono::milliseconds(timeout_ms),
1435+ [this, &state]{return state_ == state;});
1436+}
1437+
1438+void RegistryObject::ScopeProcess::kill(std::unique_lock<std::mutex>& lock)
1439+{
1440+ if (state_ == Stopped)
1441+ {
1442+ return;
1443+ }
1444+
1445+ try
1446+ {
1447+ // first try to close the scope process gracefully
1448+ process_.send_signal_or_throw(core::posix::Signal::sig_term);
1449+
1450+ if (!wait_for_state(lock, ScopeProcess::Stopped, c_process_wait_timeout))
1451+ {
1452+ std::error_code ec;
1453+
1454+ cerr << "RegistryObject::ScopeProcess::kill(): Scope: \"" << exec_data_.scope_id
1455+ << "\" is taking longer than expected to terminate gracefully. "
1456+ << "Killing the process instead." << endl;
1457+
1458+ // scope is taking too long to close, send kill signal
1459+ process_.send_signal(core::posix::Signal::sig_kill, ec);
1460+ }
1461+ }
1462+ catch (std::exception const&)
1463+ {
1464+ cerr << "RegistryObject::ScopeProcess::kill(): Failed to kill scope: \""
1465+ << exec_data_.scope_id << "\"" << endl;
1466+ throw;
1467+ }
1468+
1469+ // clear the process handle
1470+ // even on error, the previous handle will be unrecoverable at this point
1471+ clear_handle_unlocked();
1472 }
1473
1474 } // namespace internal
1475
1476=== modified file 'src/scopes/internal/RuntimeImpl.cpp'
1477--- src/scopes/internal/RuntimeImpl.cpp 2014-03-07 04:19:32 +0000
1478+++ src/scopes/internal/RuntimeImpl.cpp 2014-03-28 15:41:24 +0000
1479@@ -28,7 +28,10 @@
1480 #include <unity/scopes/ScopeExceptions.h>
1481 #include <unity/UnityExceptions.h>
1482
1483+#include <signal.h>
1484+
1485 #include <cassert>
1486+#include <cstring>
1487 #include <future>
1488 #include <iostream> // TODO: remove this once logging is added
1489
1490@@ -56,6 +59,7 @@
1491 }
1492
1493 string config_file(configfile.empty() ? DFLT_RUNTIME_INI : configfile);
1494+ configfile_ = config_file;
1495
1496 try
1497 {
1498@@ -135,6 +139,11 @@
1499 return scope_id_;
1500 }
1501
1502+string RuntimeImpl::configfile() const
1503+{
1504+ return configfile_;
1505+}
1506+
1507 MiddlewareFactory const* RuntimeImpl::factory() const
1508 {
1509 if (destroyed_.load())
1510
1511=== modified file 'src/scopes/internal/smartscopes/SSRegistryObject.cpp'
1512--- src/scopes/internal/smartscopes/SSRegistryObject.cpp 2014-03-20 16:19:04 +0000
1513+++ src/scopes/internal/smartscopes/SSRegistryObject.cpp 2014-03-28 15:41:24 +0000
1514@@ -45,12 +45,12 @@
1515
1516 SSRegistryObject::SSRegistryObject(MiddlewareBase::SPtr middleware,
1517 std::string const& ss_scope_endpoint,
1518- uint no_reply_timeout,
1519+ uint http_reply_timeout,
1520 uint refresh_rate_in_sec,
1521 std::string const& sss_url,
1522 bool caching_enabled)
1523 : ssclient_(std::make_shared<SmartScopesClient>(
1524- std::make_shared<HttpClientQt>(no_reply_timeout),
1525+ std::make_shared<HttpClientQt>(http_reply_timeout),
1526 std::make_shared<JsonCppNode>(), sss_url))
1527 , refresh_stopped_(false)
1528 , middleware_(middleware)
1529@@ -85,12 +85,12 @@
1530 refresh_thread_.join();
1531 }
1532
1533-ScopeMetadata SSRegistryObject::get_metadata(std::string const& scope_id)
1534+ScopeMetadata SSRegistryObject::get_metadata(std::string const& scope_id) const
1535 {
1536- // If the name is empty, it was sent as empty by the remote client.
1537+ // If the id is empty, it was sent as empty by the remote client.
1538 if (scope_id.empty())
1539 {
1540- throw unity::InvalidArgumentException("SSRegistryObject: Cannot search for scope with empty name");
1541+ throw unity::InvalidArgumentException("SSRegistryObject: Cannot search for scope with empty id");
1542 }
1543
1544 std::lock_guard<std::mutex> lock(scopes_mutex_);
1545@@ -103,7 +103,7 @@
1546 return it->second;
1547 }
1548
1549-MetadataMap SSRegistryObject::list()
1550+MetadataMap SSRegistryObject::list() const
1551 {
1552 std::lock_guard<std::mutex> lock(scopes_mutex_);
1553 return scopes_;
1554@@ -274,7 +274,8 @@
1555 if (changed)
1556 {
1557 // something has changed, send invalidate signal
1558- system(c_dbussend_cmd);
1559+ int result = system(c_dbussend_cmd);
1560+ (void)result;
1561 }
1562 }
1563
1564@@ -282,7 +283,7 @@
1565 {
1566 if (metadata.scope_id().empty())
1567 {
1568- throw unity::InvalidArgumentException("SSRegistryObject: Cannot add scope with empty name");
1569+ throw unity::InvalidArgumentException("SSRegistryObject: Cannot add scope with empty id");
1570 }
1571
1572 // store the base url under a scope name key
1573
1574=== modified file 'test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp'
1575--- test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp 2014-03-07 12:31:25 +0000
1576+++ test/gtest/scopes/internal/zmq_middleware/RegistryI/RegistryI_test.cpp 2014-03-28 15:41:24 +0000
1577@@ -42,6 +42,30 @@
1578 using namespace unity::scopes::internal;
1579 using namespace unity::scopes::internal::zmq_middleware;
1580
1581+namespace
1582+{
1583+struct Scope
1584+{
1585+ Scope()
1586+ : trap(core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_chld})),
1587+ death_observer(core::posix::ChildProcess::DeathObserver::create_once_with_signal_trap(trap)),
1588+ worker([this]() { trap->run(); })
1589+ {
1590+ }
1591+
1592+ ~Scope()
1593+ {
1594+ trap->stop();
1595+
1596+ if (worker.joinable())
1597+ worker.join();
1598+ }
1599+
1600+ std::shared_ptr<core::posix::SignalTrap> trap;
1601+ std::unique_ptr<core::posix::ChildProcess::DeathObserver> death_observer;
1602+ std::thread worker;
1603+} scope;
1604+
1605 ScopeMetadata make_meta(const string& name, MWScopeProxy const& proxy, MiddlewareBase::SPtr const& mw)
1606 {
1607 unique_ptr<ScopeMetadataImpl> mi(new ScopeMetadataImpl(mw.get()));
1608@@ -56,10 +80,11 @@
1609 mi->set_proxy(p);
1610 return ScopeMetadataImpl::create(move(mi));
1611 }
1612+}
1613
1614 TEST(RegistryI, get_metadata)
1615 {
1616- vector<string> dummy_spawn_command;
1617+ RegistryObject::ScopeExecData dummy_exec_data;
1618 RuntimeImpl::UPtr runtime = RuntimeImpl::create(
1619 "TestRegistry", TEST_BUILD_ROOT "/gtest/scopes/internal/zmq_middleware/RegistryI/Runtime.ini");
1620
1621@@ -70,11 +95,11 @@
1622 string mw_configfile = c.mw_configfile();
1623
1624 MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile);
1625- RegistryObject::SPtr ro(make_shared<RegistryObject>());
1626+ RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer));
1627 auto registry = middleware->add_registry_object(identity, ro);
1628 auto p = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1");
1629 EXPECT_TRUE(ro->add_local_scope("scope1", move(make_meta("scope1", p, middleware)),
1630- dummy_spawn_command));
1631+ dummy_exec_data));
1632
1633 auto r = runtime->registry();
1634 auto scope = r->get_metadata("scope1");
1635@@ -93,17 +118,17 @@
1636 string mw_configfile = c.mw_configfile();
1637
1638 MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile);
1639- RegistryObject::SPtr ro(make_shared<RegistryObject>());
1640+ RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer));
1641 auto registry = middleware->add_registry_object(identity, ro);
1642
1643 auto r = runtime->registry();
1644 auto scopes = r->list();
1645 EXPECT_TRUE(scopes.empty());
1646
1647- vector<string> dummy_spawn_command;
1648+ RegistryObject::ScopeExecData dummy_exec_data;
1649 auto proxy = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1");
1650 EXPECT_TRUE(ro->add_local_scope("scope1", move(make_meta("scope1", proxy, middleware)),
1651- dummy_spawn_command));
1652+ dummy_exec_data));
1653 scopes = r->list();
1654 EXPECT_EQ(1u, scopes.size());
1655 EXPECT_NE(scopes.end(), scopes.find("scope1"));
1656@@ -117,7 +142,7 @@
1657 {
1658 string long_id = "0000000000000000000000000000000000000000000000" + to_string(i);
1659 EXPECT_TRUE(ro->add_local_scope(long_id, move(make_meta(long_id, proxy, middleware)),
1660- dummy_spawn_command));
1661+ dummy_exec_data));
1662 ids.insert(long_id);
1663 }
1664 scopes = r->list();
1665@@ -142,22 +167,22 @@
1666 string mw_configfile = c.mw_configfile();
1667
1668 MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile);
1669- RegistryObject::SPtr ro(make_shared<RegistryObject>());
1670+ RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer));
1671 auto registry = middleware->add_registry_object(identity, ro);
1672
1673 auto r = runtime->registry();
1674 auto scopes = r->list();
1675 EXPECT_TRUE(scopes.empty());
1676
1677- vector<string> dummy_spawn_command;
1678+ RegistryObject::ScopeExecData dummy_exec_data;
1679 auto proxy = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1");
1680 EXPECT_TRUE(ro->add_local_scope("scope1", move(make_meta("scope1", proxy, middleware)),
1681- dummy_spawn_command));
1682+ dummy_exec_data));
1683 scopes = r->list();
1684 EXPECT_EQ(1u, scopes.size());
1685 EXPECT_NE(scopes.end(), scopes.find("scope1"));
1686 EXPECT_FALSE(ro->add_local_scope("scope1", move(make_meta("scope1", proxy, middleware)),
1687- dummy_spawn_command));
1688+ dummy_exec_data));
1689
1690 EXPECT_TRUE(ro->remove_local_scope("scope1"));
1691 scopes = r->list();
1692@@ -169,7 +194,7 @@
1693 {
1694 string long_id = "0000000000000000000000000000000000000000000000" + to_string(i);
1695 ro->add_local_scope(long_id, move(make_meta(long_id, proxy, middleware)),
1696- dummy_spawn_command);
1697+ dummy_exec_data);
1698 ids.insert(long_id);
1699 }
1700 scopes = r->list();
1701@@ -194,11 +219,11 @@
1702 string mw_configfile = c.mw_configfile();
1703
1704 MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile);
1705- RegistryObject::SPtr ro(make_shared<RegistryObject>());
1706- vector<string> dummy_spawn_command;
1707+ RegistryObject::SPtr ro(make_shared<RegistryObject>(*scope.death_observer));
1708+ RegistryObject::ScopeExecData dummy_exec_data;
1709 auto registry = middleware->add_registry_object(identity, ro);
1710 auto proxy = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1");
1711- ro->add_local_scope("scope1", move(make_meta("scope1", proxy, middleware)), dummy_spawn_command);
1712+ ro->add_local_scope("scope1", move(make_meta("scope1", proxy, middleware)), dummy_exec_data);
1713
1714 auto r = runtime->registry();
1715
1716@@ -221,19 +246,19 @@
1717 catch (MiddlewareException const& e)
1718 {
1719 EXPECT_STREQ("unity::scopes::MiddlewareException: unity::InvalidArgumentException: "
1720- "RegistryObject::get_metadata(): Cannot search for scope with empty name",
1721+ "RegistryObject::get_metadata(): Cannot search for scope with empty id",
1722 e.what());
1723 }
1724
1725 try
1726 {
1727 auto proxy = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1");
1728- ro->add_local_scope("", move(make_meta("blah", proxy, middleware)), dummy_spawn_command);
1729+ ro->add_local_scope("", move(make_meta("blah", proxy, middleware)), dummy_exec_data);
1730 FAIL();
1731 }
1732 catch (InvalidArgumentException const& e)
1733 {
1734- EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::add_local_scope(): Cannot add scope with empty name",
1735+ EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::add_local_scope(): Cannot add scope with empty id",
1736 e.what());
1737 }
1738
1739@@ -244,7 +269,7 @@
1740 }
1741 catch (InvalidArgumentException const& e)
1742 {
1743- EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::remove_local_scope(): Cannot remove scope with empty name",
1744+ EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::remove_local_scope(): Cannot remove scope with empty id",
1745 e.what());
1746 }
1747 }
1748@@ -255,25 +280,26 @@
1749 class MockRegistryObject : public RegistryObject
1750 {
1751 public:
1752- MockRegistryObject()
1753+ MockRegistryObject(core::posix::ChildProcess::DeathObserver& death_observer)
1754+ : RegistryObject(death_observer)
1755 {
1756 }
1757
1758- virtual ScopeProxy locate(string const& scope_name) override
1759+ virtual ScopeProxy locate(string const& scope_id) override
1760 {
1761- if (scope_name == "no_such_scope")
1762+ if (scope_id == "no_such_scope")
1763 {
1764- throw NotFoundException("no can find", scope_name);
1765+ throw NotFoundException("no can find", scope_id);
1766 }
1767- if (scope_name == "error_scope")
1768+ if (scope_id == "error_scope")
1769 {
1770 throw RegistryException("Couldn't start error_scope");
1771 }
1772- return get_metadata(scope_name).proxy();
1773+ return get_metadata(scope_id).proxy();
1774 }
1775 };
1776
1777-TEST(RegistryI, locate)
1778+TEST(RegistryI, locate_mock)
1779 {
1780 RuntimeImpl::UPtr runtime = RuntimeImpl::create(
1781 "TestRegistry", TEST_BUILD_ROOT "/gtest/scopes/internal/zmq_middleware/RegistryI/Runtime.ini");
1782@@ -283,15 +309,15 @@
1783 string mw_kind = c.mw_kind();
1784 string mw_endpoint = c.endpoint();
1785 string mw_configfile = c.mw_configfile();
1786- vector<string> dummy_spawn_command;
1787+ RegistryObject::ScopeExecData dummy_exec_data;
1788
1789 MiddlewareBase::SPtr middleware = runtime->factory()->create(identity, mw_kind, mw_configfile);
1790- MockRegistryObject::SPtr mro(make_shared<MockRegistryObject>());
1791+ MockRegistryObject::SPtr mro(make_shared<MockRegistryObject>(*scope.death_observer));
1792 auto r = middleware->add_registry_object(identity, mro);
1793 auto r_proxy = dynamic_pointer_cast<ZmqRegistry>(r);
1794 auto proxy = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1");
1795 mro->add_local_scope("scope1", move(make_meta("scope1", proxy, middleware)),
1796- dummy_spawn_command);
1797+ dummy_exec_data);
1798
1799 auto p = r_proxy->locate("scope1");
1800 EXPECT_EQ("scope1", p->identity());
1801@@ -322,12 +348,12 @@
1802 {
1803 auto proxy = middleware->create_scope_proxy("scope1", "ipc:///tmp/scope1");
1804 mro->add_local_scope("", move(make_meta("blah", proxy, middleware)),
1805- dummy_spawn_command);
1806+ dummy_exec_data);
1807 FAIL();
1808 }
1809 catch (InvalidArgumentException const& e)
1810 {
1811- EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::add_local_scope(): Cannot add scope with empty name",
1812+ EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::add_local_scope(): Cannot add scope with empty id",
1813 e.what());
1814 }
1815
1816@@ -338,7 +364,185 @@
1817 }
1818 catch (InvalidArgumentException const& e)
1819 {
1820- EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::remove_local_scope(): Cannot remove scope with empty name",
1821+ EXPECT_STREQ("unity::InvalidArgumentException: RegistryObject::remove_local_scope(): Cannot remove scope with empty id",
1822 e.what());
1823 }
1824 }
1825+
1826+std::string exec_cmd(std::string const& cmd)
1827+{
1828+ FILE* pipe = popen(cmd.c_str(), "r");
1829+ if (!pipe)
1830+ {
1831+ return "";
1832+ }
1833+
1834+ char buffer[128];
1835+ std::string result;
1836+ while (!feof(pipe))
1837+ {
1838+ if (fgets(buffer, 128, pipe) != NULL)
1839+ {
1840+ result += buffer;
1841+ }
1842+ }
1843+ pclose(pipe);
1844+
1845+ return result;
1846+}
1847+
1848+int first_child_pid()
1849+{
1850+ return stoi(exec_cmd("ps --ppid " + std::to_string(getpid()) + " --no-headers"));
1851+}
1852+
1853+int process_count()
1854+{
1855+ return stoi(exec_cmd("ps --ppid " + std::to_string(getpid()) + " | wc -l"));
1856+}
1857+
1858+TEST(RegistryI, locate)
1859+{
1860+ // get number of processes belonging to this test instance
1861+ int start_process_count = process_count();
1862+ int current_process_count = 0;
1863+ {
1864+ // configure registry
1865+ std::string rt_config = TEST_BUILD_ROOT "/gtest/scopes/internal/zmq_middleware/RegistryI/Runtime.ini";
1866+
1867+ RuntimeImpl::UPtr rt = RuntimeImpl::create("TestRegistry", rt_config);
1868+ string reg_id = rt->registry_identity();
1869+
1870+ RegistryConfig c(reg_id, rt->registry_configfile());
1871+ string mw_kind = c.mw_kind();
1872+ string scoperunner_path = c.scoperunner_path();
1873+
1874+ MiddlewareBase::SPtr mw = rt->factory()->find(reg_id, mw_kind);
1875+
1876+ RegistryObject::SPtr reg(new RegistryObject(*scope.death_observer));
1877+ mw->add_registry_object(reg_id, reg);
1878+
1879+ // configure scopes
1880+ std::array<std::string, 6> scope_ids = {"scope-A", "scope-B", "scope-C", "scope-D", "scope-N", "scope-S"};
1881+ std::map<std::string, ScopeProxy> proxies;
1882+
1883+ for (auto& scope_id : scope_ids)
1884+ {
1885+ proxies[scope_id] = ScopeImpl::create(mw->create_scope_proxy(scope_id), mw->runtime(), scope_id);
1886+
1887+ unique_ptr<ScopeMetadataImpl> mi(new ScopeMetadataImpl(mw.get()));
1888+ mi->set_scope_id(scope_id);
1889+ mi->set_display_name(scope_id);
1890+ mi->set_description(scope_id);
1891+ mi->set_author("Canonical Ltd.");
1892+ mi->set_proxy(proxies[scope_id]);
1893+ auto meta = ScopeMetadataImpl::create(move(mi));
1894+
1895+ RegistryObject::ScopeExecData exec_data;
1896+ exec_data.scope_id = scope_id;
1897+ exec_data.scoperunner_path = scoperunner_path;
1898+ exec_data.runtime_config = rt_config;
1899+ exec_data.scope_config = TEST_BUILD_ROOT "/../demo/scopes/" + scope_id + "/" + scope_id + ".ini";
1900+
1901+ reg->add_local_scope(scope_id, move(meta), exec_data);
1902+ }
1903+
1904+ // test initial state
1905+ {
1906+ // check that no scope processes are running
1907+ for (auto const& scope_id : scope_ids)
1908+ {
1909+ EXPECT_FALSE(reg->is_scope_running(scope_id));
1910+ }
1911+
1912+ // check that no new processes have been started yet
1913+ current_process_count = process_count();
1914+ EXPECT_EQ(0, current_process_count - start_process_count);
1915+ }
1916+
1917+ // test scope death and re-locate
1918+ {
1919+ // locate first scope
1920+ EXPECT_EQ(proxies[scope_ids[0]], reg->locate(scope_ids[0]));
1921+
1922+ // check that the first scope is running
1923+ EXPECT_TRUE(reg->is_scope_running(scope_ids[0]));
1924+
1925+ // check that 1 new process was started
1926+ current_process_count = process_count();
1927+ EXPECT_EQ(1, current_process_count - start_process_count);
1928+
1929+ // kill first scope
1930+ int scope1_pid = first_child_pid();
1931+ kill(scope1_pid, SIGKILL);
1932+
1933+ // wait for the SIGCHLD signal to reach the registry
1934+ while (reg->is_scope_running(scope_ids[0]))
1935+ {
1936+ std::this_thread::sleep_for(std::chrono::milliseconds{10});
1937+ }
1938+
1939+ // check that we now have no running scopes
1940+ current_process_count = process_count();
1941+ EXPECT_EQ(0, current_process_count - start_process_count);
1942+
1943+ // locate first scope
1944+ EXPECT_EQ(proxies[scope_ids[0]], reg->locate(scope_ids[0]));
1945+
1946+ // check that the first scope is running
1947+ EXPECT_TRUE(reg->is_scope_running(scope_ids[0]));
1948+
1949+ // check that 1 new process was started
1950+ current_process_count = process_count();
1951+ EXPECT_EQ(1, current_process_count - start_process_count);
1952+ }
1953+
1954+ // test locating all scopes
1955+ {
1956+ // locate all scopes (hense starting all scope processes)
1957+ for (auto const& scope_id : scope_ids)
1958+ {
1959+ EXPECT_EQ(proxies[scope_id], reg->locate(scope_id));
1960+ }
1961+
1962+ // check that all scopes processes are running
1963+ for (auto const& scope_id : scope_ids)
1964+ {
1965+ EXPECT_TRUE(reg->is_scope_running(scope_id));
1966+ }
1967+
1968+ // check that 6 new processes were started
1969+ current_process_count = process_count();
1970+ EXPECT_EQ(6, current_process_count - start_process_count);
1971+ }
1972+
1973+ // test locating the same scope multiple times
1974+ {
1975+ // locate the second scope multiple times
1976+ for (int i = 0; i < 1000; ++i)
1977+ {
1978+ EXPECT_EQ(proxies[scope_ids[1]], reg->locate(scope_ids[1]));
1979+ }
1980+
1981+ // check that no new processes were started
1982+ current_process_count = process_count();
1983+ EXPECT_EQ(6, current_process_count - start_process_count);
1984+ }
1985+
1986+ // test removing a scope
1987+ {
1988+ // remove a scope (hense killing the process)
1989+ EXPECT_TRUE(reg->remove_local_scope(scope_ids[0]));
1990+
1991+ // check that we now have 5 scopes running
1992+ current_process_count = process_count();
1993+ EXPECT_EQ(5, current_process_count - start_process_count);
1994+ }
1995+
1996+ // reg falls out of scope here and gets deleted (hense closing all scope processes)
1997+ }
1998+
1999+ // check that we are back to the original number of processes
2000+ current_process_count = process_count();
2001+ EXPECT_EQ(0, current_process_count - start_process_count);
2002+}
2003
2004=== modified file 'test/gtest/scopes/internal/zmq_middleware/RegistryI/TestRegistry.ini.in'
2005--- test/gtest/scopes/internal/zmq_middleware/RegistryI/TestRegistry.ini.in 2014-01-14 12:54:15 +0000
2006+++ test/gtest/scopes/internal/zmq_middleware/RegistryI/TestRegistry.ini.in 2014-03-28 15:41:24 +0000
2007@@ -7,4 +7,4 @@
2008 Zmq.EndpointDir = /tmp
2009 Zmq.ConfigFile = Zmq.ini
2010 Scope.InstallDir = /tmp
2011-Scoperunner.Path = /tmp
2012+Scoperunner.Path = @CMAKE_BINARY_DIR@/scoperunner/scoperunner

Subscribers

People subscribed via source and target branches

to all changes: