Merge lp:~marcustomlinson/unity-scopes-api/scope_process_lifetime into lp:unity-scopes-api/devel
- scope_process_lifetime
- Merge into devel
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 |
Related bugs: |
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.
Marcus Tomlinson (marcustomlinson) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:265
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 266. By Marcus Tomlinson
-
Fixed RegistryI_test
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:266
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
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?
Paweł Stołowski (stolowski) wrote : | # |
661 +void RegistryObject:
662 +{
..
665 + if (state() == ScopeProcess:
..
670 + else if (state() == ScopeProcess:
..
681 + update_
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:
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?
Paweł Stołowski (stolowski) wrote : | # |
711 + if (!wait_
712 + {
713 + kill();
714 + throw unity::
715 + + exec_data_.scope_id + "\" took too long to start.");
716 + }
Please consider catching exception from kill() here.
Paweł Stołowski (stolowski) wrote : | # |
694 + process_ = core::posix:
I think we should pass stdout|stderr, or otherwise we'll loose all error messages from scopes, no?
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.
Marcus Tomlinson (marcustomlinson) wrote : | # |
> 720 + // 4. add the scope process to the death observer
> 721 +
> core::posix:
> 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() .
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:271
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:273
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Marcus Tomlinson (marcustomlinson) wrote : | # |
Alright, I think I've addressed all review comments so far.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:275
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 276. By Marcus Tomlinson
-
Updated symbols file.
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?
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 :-(
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:276
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 277. By Marcus Tomlinson
-
in_lock_* methods renamed to *_unlocked
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:277
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 278. By Marcus Tomlinson
-
Detach from death_observer_
thread_ if quit throws.
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:278
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
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
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:280
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 283. By Marcus Tomlinson
-
Some formatting fixes
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:282
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:283
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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?
- 284. By Marcus Tomlinson
-
Added checks for scope process deaths to locate test.
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:284
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 286. By Marcus Tomlinson
-
Wait for scope to properly die after calling kill in locate test.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:285
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
This looks good to me! Thanks!
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:286
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 287. By Marcus Tomlinson
-
Wait for the SIGCHLD signal to reach the registry
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:287
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) 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.
pawel@trusty ~/src/ubuntu/
scoperegistry: unity::
scoperegistry: could not open OEM installation directory, ignoring OEM scopes
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
^CRegistryObjec
RegistryObject:
pawel@trusty ~/src/ubuntu/
scoperegistry: unity::
scoperegistry: could not open OEM installation directory, ignoring OEM scopes
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
~ObjectAdapter(): exception from shutdown(): unity::
~ObjectAdapter(): exception from wait_for_
unexpected exception in add_scope_object(): unity::
unity:
~ObjectAdapter(): exception from shutdown(): unity::
~ObjectAdapter(): exception from wait_for_
unexpected exception in add_scope_object(): unity::
Michal Hruby (mhr3) wrote : | # |
948 + while (state_ != state && time_left > 0)
949 + {
950 + auto start = std::chrono:
951 + state_change_
952 +
953 + // update time left
954 + time_left -= std::chrono:
955 + std::chrono:
956 + }
Why so complicated? condvar.
Why don't the clear_handle_
Michal Hruby (mhr3) wrote : | # |
942 +bool RegistryObject:
Also, this is a bit confusing, has the _unlocked suffix, yet it drops the mutex, when waiting for the condvar.
Marcus Tomlinson (marcustomlinson) wrote : | # |
> 948 + while (state_ != state && time_left > 0)
> 949 + {
> 950 + auto start = std::chrono:
> Why so complicated? condvar.
> 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_
> 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.
Marcus Tomlinson (marcustomlinson) wrote : | # |
> 942 +bool RegistryObject:
> 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.
Michal Hruby (mhr3) wrote : | # |
> > 942 +bool
> RegistryObject:
> > 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.
Marcus Tomlinson (marcustomlinson) wrote : | # |
> 948 + while (state_ != state && time_left > 0)
> 949 + {
> 950 + auto start = std::chrono:
> Why so complicated? condvar.
> 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.
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
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:289
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:292
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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:
^CRegistryObjec
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
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.
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
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?
- 293. By Marcus Tomlinson
-
Added internal SigTermHandler class to handle SIGTERM signal in scoperegistry, scoperunner and smartscopesproxy.
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:
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:293
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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
> 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)
Michal Hruby (mhr3) wrote : | # |
8 - (c++)"unity:
10 + (c++)"unity:
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
Marcus Tomlinson (marcustomlinson) wrote : | # |
> 8 - (c++)"unity:
> std::char_
> 0.4.0+14.
> 10 + (c++)"unity:
> std::char_
>
> 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:294
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 295. By Marcus Tomlinson
-
Updated RegistryObject:
:ScopeProcess: :state( ) to const
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:295
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 296. By Marcus Tomlinson
-
Updated symbols
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:296
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:297
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 300. By Marcus Tomlinson
-
Fixed bzr bd issues
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:299
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:300
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
1932 + unity::
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.
- 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
Marcus Tomlinson (marcustomlinson) wrote : | # |
> 1932 + unity::
>
> 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:302
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:303
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michal Hruby (mhr3) wrote : | # |
Looks good to me now, still need to wait for process-cpp though.
- 304. By Marcus Tomlinson
-
Merged devel
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:304
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 305. By Marcus Tomlinson
-
Merged trunk and resolved conflicts
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:305
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 306. By Marcus Tomlinson
-
Clear the sig mask (inherited from scoperegistry) on entry.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:306
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 307. By Marcus Tomlinson
-
Merged trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:307
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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/
scoperegistry: unity::
scoperegistry: could not open OEM installation directory, ignoring OEM scopes
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
RegistryObject:
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/
Scope metadata:
scope_id: scope-A
art: scope-A.Art
icon: scope-A.Icon
hot_key: scope-A.HotKey
query complete, status: error: unity::
unity::
And the scope is not started.
NB, I'm running it with the fix_env_var_split branc of libprocess-cpp.
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/
> ../scoperegistr
> scoperegistry: unity::
> directory "/custom/
> directory
> scoperegistry: could not open OEM installation directory, ignoring OEM scopes
> RegistryObject:
> RegistryObject:
> RegistryObject:
> RegistryObject:
> RegistryObject:
> RegistryObject:
> RegistryObject:
> 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/
> ./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::
> out after 300 milliseconds
> unity::
>
> 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:308
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
OK, I run this on the phone and didn't spot any regressions. Let's get it in.
Preview Diff
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 |
The changes to process-cpp required for this branch have now landed. Ready for review.