Merge lp:unity-scopes-api/staging into lp:unity-scopes-api
- staging
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Paweł Stołowski |
Approved revision: | 294 |
Merged at revision: | 334 |
Proposed branch: | lp:unity-scopes-api/staging |
Merge into: | lp:unity-scopes-api |
Diff against target: |
794 lines (+179/-312) 14 files modified
CONFIGFILES (+16/-16) RELEASE_NOTES.md (+4/-0) include/unity/scopes/ScopeBase.h (+2/-0) include/unity/scopes/internal/RegistryObject.h (+0/-7) include/unity/scopes/internal/SettingsDB.h (+4/-21) include/unity/scopes/internal/zmq_middleware/ZmqObjectProxy.h (+3/-1) scoperegistry/scoperegistry.cpp (+2/-2) src/scopes/internal/RegistryObject.cpp (+16/-41) src/scopes/internal/SettingsDB.cpp (+64/-199) src/scopes/internal/zmq_middleware/ZmqObject.cpp (+9/-9) src/scopes/internal/zmq_middleware/ZmqScope.cpp (+14/-4) test/gtest/scopes/Registry/Registry_test.cpp (+1/-1) test/gtest/scopes/internal/CMakeLists.txt (+1/-1) test/gtest/scopes/internal/SettingsDB/SettingsDB_test.cpp (+43/-10) |
To merge this branch: | bzr merge lp:unity-scopes-api/staging |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Unity Team | Pending | ||
Review via email:
|
Commit message
Fix handling of timeout in debug mode.
Use mtime instead of inotify to detect settings changes.
Revert delayed notification about scope removal.
Description of the change
Merged devel:
Fix handling of timeout in debug mode.
Use mtime instead of inotify to detect settings changes.
Revert delayed notification about scope removal.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
- 293. By Paweł Stołowski
-
Merged devel
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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://
- 294. By Paweł Stołowski
-
Merged devel
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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://
Preview Diff
1 | === modified file 'CONFIGFILES' |
2 | --- CONFIGFILES 2015-06-11 11:11:42 +0000 |
3 | +++ CONFIGFILES 2015-07-08 14:34:02 +0000 |
4 | @@ -76,22 +76,6 @@ |
5 | |
6 | The default value is 10 seconds. |
7 | |
8 | -- Process.Timeout |
9 | - |
10 | - This parameter determines how the registry will wait for a scope |
11 | - to start and stop. The registry waits for at most Process.Timeout |
12 | - milliseconds before it concludes that a scope failed to initialize |
13 | - or terminate correctly. If a scope does not respond |
14 | - within the allotted time, it is killed with SIGKILL. |
15 | - |
16 | - Only values in the range 10 to 15000 milliseconds are accepted. |
17 | - |
18 | - The default value is 4000 milliseconds. |
19 | - |
20 | - Note that this value must be less than Locate.Timeout in the [<Middleware>] |
21 | - config group, otherwise the middleware can prematurely conclude that |
22 | - a locate() request failed to start a scope. |
23 | - |
24 | - CacheDir |
25 | |
26 | The parent directory under which a scope can write scope-specific data files |
27 | @@ -306,6 +290,22 @@ |
28 | |
29 | The default value is "$HOME/.local/share/unity-scopes". |
30 | |
31 | +- Process.Timeout |
32 | + |
33 | + This parameter determines how long the registry will wait for a scope |
34 | + to start and stop. The registry waits for at most Process.Timeout |
35 | + milliseconds before it concludes that a scope failed to initialize |
36 | + or terminate correctly. If a scope does not respond |
37 | + within the allotted time, it is killed with SIGKILL. |
38 | + |
39 | + Only values in the range 10 to 15000 milliseconds are accepted. |
40 | + |
41 | + The default value is 4000 milliseconds. |
42 | + |
43 | + Note that this value must be less than Locate.Timeout in the [<Middleware>] |
44 | + config group, otherwise the middleware can prematurely conclude that |
45 | + a locate() request failed to start a scope. |
46 | + |
47 | |
48 | Smartscopes.ini |
49 | -------------- |
50 | |
51 | === modified file 'RELEASE_NOTES.md' |
52 | --- RELEASE_NOTES.md 2015-06-01 10:15:48 +0000 |
53 | +++ RELEASE_NOTES.md 2015-07-08 14:34:02 +0000 |
54 | @@ -1,6 +1,10 @@ |
55 | Release notes |
56 | ============= |
57 | |
58 | +Changes in version 0.6.19 |
59 | +========================= |
60 | + - Support UNITY_SCOPES_CONFIG_DIR environment variable. |
61 | + |
62 | Changes in version 0.6.18 |
63 | ========================= |
64 | - Allow child_scopes() and set_child_scopes() methods more time to |
65 | |
66 | === modified file 'include/unity/scopes/ScopeBase.h' |
67 | --- include/unity/scopes/ScopeBase.h 2015-06-02 05:06:25 +0000 |
68 | +++ include/unity/scopes/ScopeBase.h 2015-07-08 14:34:02 +0000 |
69 | @@ -326,6 +326,8 @@ |
70 | |
71 | \return The scope's current settings. |
72 | \throws LogicException if called from the constructor of this instance. |
73 | + \throws ResourceException if settings database file is corrupted. |
74 | + \throws FileException if settings database file is not readable. |
75 | */ |
76 | virtual VariantMap settings() const final; |
77 | |
78 | |
79 | === modified file 'include/unity/scopes/internal/RegistryObject.h' |
80 | --- include/unity/scopes/internal/RegistryObject.h 2015-04-23 10:11:51 +0000 |
81 | +++ include/unity/scopes/internal/RegistryObject.h 2015-07-08 14:34:02 +0000 |
82 | @@ -29,7 +29,6 @@ |
83 | #include <condition_variable> |
84 | #include <mutex> |
85 | #include <thread> |
86 | -#include <chrono> |
87 | |
88 | namespace unity |
89 | { |
90 | @@ -153,12 +152,6 @@ |
91 | mutable std::mutex mutex_; |
92 | |
93 | MWPublisher::SPtr publisher_; |
94 | - std::thread publisher_notify_thread_; |
95 | - std::condition_variable publisher_notify_cond_; |
96 | - std::chrono::system_clock::time_point publisher_notify_timepoint_; |
97 | - bool publisher_notify_reset_timer_; |
98 | - bool publisher_notify_exit_; |
99 | - |
100 | MWSubscriber::SPtr ss_list_update_subscriber_; |
101 | std::shared_ptr<core::ScopedConnection> ss_list_update_connection_; |
102 | bool generate_desktop_files_; |
103 | |
104 | === modified file 'include/unity/scopes/internal/SettingsDB.h' |
105 | --- include/unity/scopes/internal/SettingsDB.h 2014-11-20 05:32:53 +0000 |
106 | +++ include/unity/scopes/internal/SettingsDB.h 2015-07-08 14:34:02 +0000 |
107 | @@ -25,11 +25,7 @@ |
108 | #include <unity/util/NonCopyable.h> |
109 | #include <unity/util/ResourcePtr.h> |
110 | |
111 | -#include <sys/inotify.h> |
112 | -#include <sys/ioctl.h> |
113 | - |
114 | -#include <atomic> |
115 | -#include <thread> |
116 | +#include <time.h> |
117 | |
118 | namespace unity |
119 | { |
120 | @@ -61,7 +57,7 @@ |
121 | unity::scopes::internal::SettingsSchema const& schema, |
122 | boost::log::sources::severity_channel_logger_mt<>& logger); |
123 | |
124 | - ~SettingsDB(); |
125 | + ~SettingsDB() = default; |
126 | |
127 | SettingsDB(SettingsDB&&) = default; |
128 | SettingsDB& operator=(SettingsDB&&) = default; |
129 | @@ -69,14 +65,6 @@ |
130 | VariantMap settings(); // Returns the current settings (checking the DB each time). |
131 | |
132 | private: |
133 | - enum ThreadState |
134 | - { |
135 | - Idle, |
136 | - Running, |
137 | - Stopping, |
138 | - Failed |
139 | - }; |
140 | - |
141 | SettingsDB(std::string const& db_path, |
142 | unity::scopes::internal::SettingsSchema const& schema, |
143 | boost::log::sources::severity_channel_logger_mt<>& logger); |
144 | @@ -84,18 +72,13 @@ |
145 | void process_doc_(std::string const& id, unity::util::IniParser const& parer); |
146 | void process_all_docs(); |
147 | void set_defaults(); |
148 | - void watch_thread(); |
149 | |
150 | - bool state_changed_; |
151 | std::string db_path_; |
152 | - unity::util::ResourcePtr<int, std::function<void(int)>> fd_; |
153 | - unity::util::ResourcePtr<int, std::function<void(int)>> watch_; |
154 | + decltype(::timespec::tv_nsec) last_write_time_; |
155 | + ::ino_t last_write_inode_; |
156 | VariantArray definitions_; // Returned by SettingsSchema |
157 | std::map<std::string, Variant> def_map_; // Allows fast access to the Variants in definitions_ |
158 | unity::scopes::VariantMap values_; |
159 | - std::thread thread_; |
160 | - std::mutex mutex_; |
161 | - ThreadState thread_state_; |
162 | boost::log::sources::severity_channel_logger_mt<>& logger_; |
163 | }; |
164 | |
165 | |
166 | === modified file 'include/unity/scopes/internal/zmq_middleware/ZmqObjectProxy.h' |
167 | --- include/unity/scopes/internal/zmq_middleware/ZmqObjectProxy.h 2015-02-09 02:23:22 +0000 |
168 | +++ include/unity/scopes/internal/zmq_middleware/ZmqObjectProxy.h 2015-07-08 14:34:02 +0000 |
169 | @@ -91,8 +91,10 @@ |
170 | |
171 | TwowayOutParams invoke_twoway_(capnp::MessageBuilder& request); |
172 | TwowayOutParams invoke_twoway_(capnp::MessageBuilder& request, |
173 | + int64_t twoway_timeout); |
174 | + TwowayOutParams invoke_twoway_(capnp::MessageBuilder& request, |
175 | int64_t twoway_timeout, |
176 | - int64_t locate_timeout = -1); |
177 | + int64_t locate_timeout); |
178 | |
179 | private: |
180 | TwowayOutParams invoke_twoway__(capnp::MessageBuilder& request, int64_t timeout); |
181 | |
182 | === modified file 'scoperegistry/scoperegistry.cpp' |
183 | --- scoperegistry/scoperegistry.cpp 2015-04-10 03:55:25 +0000 |
184 | +++ scoperegistry/scoperegistry.cpp 2015-07-08 14:34:02 +0000 |
185 | @@ -423,10 +423,10 @@ |
186 | scope_dir.filename().native(); |
187 | } |
188 | |
189 | - // Check if this scope has requested debug mode, if so, set the process timeout to 15s |
190 | + // Check if this scope has requested debug mode, if so, disable process timeout |
191 | if (sc.debug_mode()) |
192 | { |
193 | - exec_data.timeout_ms = 15000; |
194 | + exec_data.timeout_ms = -1; |
195 | } |
196 | else |
197 | { |
198 | |
199 | === modified file 'src/scopes/internal/RegistryObject.cpp' |
200 | --- src/scopes/internal/RegistryObject.cpp 2015-04-23 10:11:51 +0000 |
201 | +++ src/scopes/internal/RegistryObject.cpp 2015-07-08 14:34:02 +0000 |
202 | @@ -37,7 +37,6 @@ |
203 | |
204 | static const char* c_debug_dbus_started_cmd = "dbus-send --type=method_call --dest=com.ubuntu.SDKAppLaunch /ScopeRegistryCallback com.ubuntu.SDKAppLaunch.ScopeLoaded"; |
205 | static const char* c_debug_dbus_stopped_cmd = "dbus-send --type=method_call --dest=com.ubuntu.SDKAppLaunch /ScopeRegistryCallback com.ubuntu.SDKAppLaunch.ScopeStopped"; |
206 | -static const std::chrono::seconds removal_notification_delay(5); |
207 | |
208 | namespace unity |
209 | { |
210 | @@ -74,8 +73,6 @@ |
211 | }) |
212 | }, |
213 | executor_(executor), |
214 | - publisher_notify_reset_timer_(false), |
215 | - publisher_notify_exit_(false), |
216 | generate_desktop_files_(generate_desktop_files) |
217 | { |
218 | if (middleware) |
219 | @@ -102,14 +99,10 @@ |
220 | |
221 | RegistryObject::~RegistryObject() |
222 | { |
223 | - if (publisher_notify_thread_.joinable()) |
224 | { |
225 | - { |
226 | - lock_guard<decltype(mutex_)> lock(mutex_); |
227 | - publisher_notify_exit_ = true; |
228 | - publisher_notify_cond_.notify_one(); |
229 | - } |
230 | - publisher_notify_thread_.join(); |
231 | + // The destructor may be called from an arbitrary |
232 | + // thread, so we need a full fence here. |
233 | + lock_guard<decltype(mutex_)> lock(mutex_); |
234 | } |
235 | |
236 | // kill all scope processes |
237 | @@ -329,34 +322,8 @@ |
238 | |
239 | if (publisher_ && erased) |
240 | { |
241 | - // Send a blank message to subscribers to inform them that the registry has been updated. |
242 | - // Delay notification so that scope is not seen as removed and then added when updated. |
243 | - lock_guard<decltype(mutex_)> lock(mutex_); |
244 | - |
245 | - if (!publisher_notify_thread_.joinable()) |
246 | - { |
247 | - publisher_notify_timepoint_ = chrono::system_clock::now() + removal_notification_delay; |
248 | - publisher_notify_thread_ = thread([this] |
249 | - { |
250 | - unique_lock<decltype(mutex_)> lock(mutex_); |
251 | - while (!publisher_notify_exit_) |
252 | - { |
253 | - auto pred = [this] |
254 | - { |
255 | - return publisher_notify_exit_ || publisher_notify_reset_timer_; |
256 | - }; |
257 | - if (!publisher_notify_cond_.wait_until(lock, publisher_notify_timepoint_, pred)) // pred is false, but timeout reached |
258 | - { |
259 | - publisher_->send_message(""); |
260 | - publisher_notify_timepoint_ = chrono::system_clock::time_point::max(); |
261 | - } |
262 | - publisher_notify_reset_timer_ = false; |
263 | - } |
264 | - }); |
265 | - } |
266 | - publisher_notify_reset_timer_ = true; |
267 | - publisher_notify_timepoint_ = chrono::system_clock::now() + removal_notification_delay; |
268 | - publisher_notify_cond_.notify_one(); |
269 | + // Send a blank message to subscribers to inform them that the registry has been updated |
270 | + publisher_->send_message(""); |
271 | } |
272 | |
273 | if (ex) |
274 | @@ -736,9 +703,17 @@ |
275 | |
276 | bool RegistryObject::ScopeProcess::wait_for_state(std::unique_lock<std::mutex>& lock, ProcessState state) const |
277 | { |
278 | - return state_change_cond_.wait_for(lock, |
279 | - std::chrono::milliseconds(exec_data_.timeout_ms), |
280 | - [this, &state]{return state_ == state;}); |
281 | + if (exec_data_.timeout_ms == -1) |
282 | + { |
283 | + state_change_cond_.wait(lock, [this, &state]{return state_ == state;}); |
284 | + return true; |
285 | + } |
286 | + else |
287 | + { |
288 | + return state_change_cond_.wait_for(lock, |
289 | + std::chrono::milliseconds(exec_data_.timeout_ms), |
290 | + [this, &state]{return state_ == state;}); |
291 | + } |
292 | } |
293 | |
294 | void RegistryObject::ScopeProcess::kill(std::unique_lock<std::mutex>& lock) |
295 | |
296 | === modified file 'src/scopes/internal/SettingsDB.cpp' |
297 | --- src/scopes/internal/SettingsDB.cpp 2015-05-08 08:31:08 +0000 |
298 | +++ src/scopes/internal/SettingsDB.cpp 2015-07-08 14:34:02 +0000 |
299 | @@ -28,6 +28,9 @@ |
300 | #include <boost/filesystem.hpp> |
301 | |
302 | #include <fcntl.h> |
303 | +#include <sys/types.h> |
304 | +#include <sys/stat.h> |
305 | +#include <unistd.h> |
306 | |
307 | using namespace unity::util; |
308 | using namespace std; |
309 | @@ -75,14 +78,6 @@ |
310 | return file_lock; |
311 | } |
312 | |
313 | -static void watch_deleter(int fd, int watch) |
314 | -{ |
315 | - if (fd >= 0 && watch >= 0) |
316 | - { |
317 | - inotify_rm_watch(fd, watch); |
318 | - } |
319 | -} |
320 | - |
321 | static const char* GROUP_NAME = "General"; |
322 | |
323 | } // namespace |
324 | @@ -129,20 +124,11 @@ |
325 | SettingsDB::SettingsDB(string const& db_path, |
326 | SettingsSchema const& schema, |
327 | boost::log::sources::severity_channel_logger_mt<>& logger) |
328 | - : state_changed_(false) |
329 | - , db_path_(db_path) |
330 | - , fd_(inotify_init(), bind(&close, placeholders::_1)) |
331 | - , watch_(-1, bind(&watch_deleter, fd_.get(), placeholders::_1)) |
332 | - , thread_state_(Idle) |
333 | + : db_path_(db_path) |
334 | + , last_write_time_(-1) |
335 | + , last_write_inode_(0) |
336 | , logger_(logger) |
337 | { |
338 | - // Validate the file descriptor |
339 | - if (fd_.get() < 0) |
340 | - { |
341 | - throw SyscallException("SettingsDB(): inotify_init() failed on inotify fd (fd = " + |
342 | - to_string(fd_.get()) + ")", errno); |
343 | - } |
344 | - |
345 | // Initialize the def_map_ so we can look things |
346 | // up quickly. |
347 | definitions_ = schema.definitions(); |
348 | @@ -150,130 +136,6 @@ |
349 | { |
350 | def_map_.emplace(make_pair(d.get_dict()["id"].get_string(), d)); |
351 | } |
352 | - |
353 | - process_all_docs(); |
354 | -} |
355 | - |
356 | -SettingsDB::~SettingsDB() |
357 | -{ |
358 | - // Tell the thread to stop politely |
359 | - { |
360 | - lock_guard<mutex> lock(mutex_); |
361 | - // Important to stop the watch, as this unblocks the select() call |
362 | - watch_.dealloc(); |
363 | - thread_state_ = ThreadState::Stopping; |
364 | - } |
365 | - |
366 | - // Wait for thread to terminate |
367 | - if (thread_.joinable()) { |
368 | - thread_.join(); |
369 | - } |
370 | -} |
371 | - |
372 | -void SettingsDB::watch_thread() |
373 | -{ |
374 | - try |
375 | - { |
376 | - fd_set fds; |
377 | - FD_ZERO(&fds); |
378 | - |
379 | -#pragma GCC diagnostic push |
380 | -#pragma GCC diagnostic ignored "-Wold-style-cast" |
381 | - FD_SET(fd_.get(), &fds); |
382 | -#pragma GCC diagnostic pop |
383 | - |
384 | - int bytes_avail = 0; |
385 | - string buffer; |
386 | - |
387 | - // Poll for notifications until stop is requested |
388 | - while (true) |
389 | - { |
390 | - // Wait for a payload to arrive |
391 | - int ret = select(fd_.get() + 1, &fds, nullptr, nullptr, nullptr); |
392 | - if (ret < 0) |
393 | - { |
394 | - throw SyscallException("SettingsDB::watch_thread(): Thread aborted: " |
395 | - "select() failed on inotify fd (fd = " + |
396 | - to_string(fd_.get()) + ")", errno); |
397 | - } |
398 | - |
399 | - // Get number of bytes available |
400 | - ret = ioctl(fd_.get(), FIONREAD, &bytes_avail); |
401 | - if (ret < 0) |
402 | - { |
403 | - throw SyscallException("SettingsDB::watch_thread(): Thread aborted: " |
404 | - "ioctl() failed on inotify fd (fd = " + |
405 | - to_string(fd_.get()) + ")", errno); |
406 | - } |
407 | - |
408 | - // Read available bytes |
409 | - buffer.resize(bytes_avail); |
410 | - int bytes_read = read(fd_.get(), &buffer[0], buffer.size()); |
411 | - if (bytes_read < 0) |
412 | - { |
413 | - throw SyscallException("SettingsDB::watch_thread(): Thread aborted: " |
414 | - "read() failed on inotify fd (fd = " + |
415 | - to_string(fd_.get()) + ")", errno); |
416 | - } |
417 | - if (bytes_read != bytes_avail) |
418 | - { |
419 | - throw ResourceException("SettingsDB::watch_thread(): Thread aborted: " |
420 | - "read() returned " + std::to_string(bytes_read) + |
421 | - " bytes, expected " + std::to_string(bytes_avail) + |
422 | - " bytes (fd = " + std::to_string(fd_.get()) + ")"); |
423 | - } |
424 | - |
425 | - // Process event(s) received |
426 | - int i = 0; |
427 | - while (i < bytes_read) |
428 | - { |
429 | - static_assert(std::alignment_of<char*>::value >= std::alignment_of<struct inotify_event>::value, |
430 | - "cannot use std::string as buffer for inotify events"); |
431 | -#pragma GCC diagnostic push |
432 | -#pragma GCC diagnostic ignored "-Wcast-align" |
433 | - auto event = reinterpret_cast<inotify_event const*>(&buffer[i]); |
434 | -#pragma GCC diagnostic pop |
435 | - |
436 | - if (event->mask & IN_DELETE_SELF) |
437 | - { |
438 | - lock_guard<mutex> lock(mutex_); |
439 | - state_changed_ = false; |
440 | - watch_.dealloc(); |
441 | - watch_.reset(-1); |
442 | - thread_state_ = Stopping; |
443 | - } |
444 | - else if (event->mask & IN_CLOSE_WRITE) |
445 | - { |
446 | - lock_guard<mutex> lock(mutex_); |
447 | - state_changed_ = true; |
448 | - } |
449 | - i += sizeof(inotify_event) + event->len; |
450 | - } |
451 | - |
452 | - |
453 | - // Break from the loop if we are stopping |
454 | - { |
455 | - lock_guard<mutex> lock(mutex_); |
456 | - if (thread_state_ == Stopping) |
457 | - { |
458 | - thread_state_ = Idle; |
459 | - break; |
460 | - } |
461 | - } |
462 | - } |
463 | - } |
464 | - catch (exception const& e) |
465 | - { |
466 | - BOOST_LOG(logger_) << "SettingsDB::watch_thread(): Thread aborted: " << e.what(); |
467 | - lock_guard<mutex> lock(mutex_); |
468 | - thread_state_ = Failed; |
469 | - } |
470 | - catch (...) |
471 | - { |
472 | - BOOST_LOG(logger_) << "SettingsDB::watch_thread(): Thread aborted: unknown exception"; |
473 | - lock_guard<mutex> lock(mutex_); |
474 | - thread_state_ = Failed; |
475 | - } |
476 | } |
477 | |
478 | // Called once for each setting. We are lenient when parsing |
479 | @@ -326,70 +188,73 @@ |
480 | |
481 | void SettingsDB::process_all_docs() |
482 | { |
483 | - lock_guard<mutex> lock(mutex_); |
484 | - |
485 | - if (!watch_ || watch_.get() < 0) |
486 | + try |
487 | { |
488 | - boost::filesystem::path p(db_path_); |
489 | - if (boost::filesystem::exists(p)) |
490 | + struct stat st; |
491 | + bool file_exists = ::stat(db_path_.c_str(), &st) == 0 && S_ISREG(st.st_mode); |
492 | + |
493 | + if (file_exists) |
494 | { |
495 | - watch_.reset(inotify_add_watch(fd_.get(), |
496 | - db_path_.c_str(), |
497 | - IN_CLOSE_WRITE |
498 | - | IN_DELETE_SELF)); |
499 | - if (watch_.get() < 0) |
500 | - { |
501 | - throw SyscallException("SettingsDB::add_watch(): failed to add watch for path: \"" + |
502 | - db_path_ + "\". inotify_add_watch() failed. (fd = " + |
503 | - to_string(fd_.get()) + ", path = " + db_path_ + ")", errno); |
504 | - } |
505 | - |
506 | - state_changed_ = true; |
507 | - |
508 | - if (thread_state_ == Idle) |
509 | - { |
510 | - if (thread_.joinable()) { |
511 | - thread_.join(); |
512 | + FileLock lock = unix_lock(db_path_); |
513 | + if (::fstat(lock.get(), &st) == 0) // re-stat the file |
514 | + { |
515 | + auto const wt = st.st_mtim.tv_nsec; |
516 | + |
517 | + if (wt != last_write_time_ || st.st_ino != last_write_inode_) |
518 | + { |
519 | + // We re-establish the defaults and re-read everything. We need to put the defaults back because |
520 | + // settings may have been deleted from the database. |
521 | + set_defaults(); |
522 | + |
523 | + try |
524 | + { |
525 | + IniParser p(db_path_.c_str()); |
526 | + |
527 | + if (p.has_group(GROUP_NAME)) |
528 | + { |
529 | + auto keys = p.get_keys(GROUP_NAME); |
530 | + for (auto const& key : keys) { |
531 | + process_doc_(key, p); |
532 | + } |
533 | + } |
534 | + } |
535 | + catch (FileException const& e) |
536 | + { |
537 | + if (e.error() == EACCES) // very unlikely; only if permissions changed after we acquired the lock |
538 | + { |
539 | + throw e; |
540 | + } |
541 | + throw ResourceException(e.what()); |
542 | + } |
543 | + |
544 | + last_write_time_ = wt; |
545 | + last_write_inode_ = st.st_ino; |
546 | + |
547 | + return; |
548 | } |
549 | - |
550 | - thread_state_ = Running; |
551 | - thread_ = thread(&SettingsDB::watch_thread, this); |
552 | } |
553 | } |
554 | else |
555 | { |
556 | - state_changed_ = false; |
557 | - |
558 | set_defaults(); |
559 | - return; |
560 | - } |
561 | - } |
562 | - |
563 | - if (state_changed_) |
564 | - { |
565 | - state_changed_ = false; |
566 | - // We re-establish the defaults and re-read everything. We need to put the defaults back because |
567 | - // settings may have been deleted from the database. |
568 | + } |
569 | + } |
570 | + catch (FileException const& e) |
571 | + { |
572 | + if (e.error() == EACCES) |
573 | + { |
574 | + throw e; |
575 | + } |
576 | + |
577 | + // Failure in obtaining the lock shouldn't be reported to the scope, it's not fatal; |
578 | + // instead give the last known values (or defaults) back. |
579 | + } |
580 | + |
581 | + // Only set default values if we don't have some values already; we might have failed because |
582 | + // of a temporary issue and therefore we want to present most recent cached settings. |
583 | + if (values_.size() == 0) |
584 | + { |
585 | set_defaults(); |
586 | - |
587 | - try |
588 | - { |
589 | - FileLock lock = unix_lock(db_path_); |
590 | - |
591 | - IniParser p(db_path_.c_str()); |
592 | - |
593 | - if (p.has_group(GROUP_NAME)) |
594 | - { |
595 | - auto keys = p.get_keys(GROUP_NAME); |
596 | - for (auto const& key : keys) { |
597 | - process_doc_(key, p); |
598 | - } |
599 | - } |
600 | - } |
601 | - catch (FileException & e) |
602 | - { |
603 | - throw ResourceException(e.what()); |
604 | - } |
605 | } |
606 | } |
607 | |
608 | |
609 | === modified file 'src/scopes/internal/zmq_middleware/ZmqObject.cpp' |
610 | --- src/scopes/internal/zmq_middleware/ZmqObject.cpp 2015-01-06 01:05:48 +0000 |
611 | +++ src/scopes/internal/zmq_middleware/ZmqObject.cpp 2015-07-08 14:34:02 +0000 |
612 | @@ -190,7 +190,13 @@ |
613 | |
614 | ZmqObjectProxy::TwowayOutParams ZmqObjectProxy::invoke_twoway_(capnp::MessageBuilder& request) |
615 | { |
616 | - return invoke_twoway_(request, timeout_); |
617 | + return invoke_twoway_(request, timeout_, mw_base()->locate_timeout()); |
618 | +} |
619 | + |
620 | +ZmqObjectProxy::TwowayOutParams ZmqObjectProxy::invoke_twoway_(capnp::MessageBuilder& request, |
621 | + int64_t twoway_timeout) |
622 | +{ |
623 | + return invoke_twoway_(request, twoway_timeout, mw_base()->locate_timeout()); |
624 | } |
625 | |
626 | ZmqObjectProxy::TwowayOutParams ZmqObjectProxy::invoke_twoway_(capnp::MessageBuilder& request, |
627 | @@ -211,14 +217,8 @@ |
628 | try |
629 | { |
630 | ObjectProxy new_proxy; |
631 | - if (locate_timeout != -1) |
632 | - { |
633 | - new_proxy = registry_proxy->locate(identity(), locate_timeout); |
634 | - } |
635 | - else |
636 | - { |
637 | - new_proxy = registry_proxy->locate(identity()); |
638 | - } |
639 | + new_proxy = registry_proxy->locate(identity(), locate_timeout); |
640 | + |
641 | // update our proxy with the newly received data |
642 | // (we need to first store values in local variables outside of the mutex, |
643 | // otherwise we will deadlock on the following ZmqObjectProxy methods) |
644 | |
645 | === modified file 'src/scopes/internal/zmq_middleware/ZmqScope.cpp' |
646 | --- src/scopes/internal/zmq_middleware/ZmqScope.cpp 2015-05-29 12:02:54 +0000 |
647 | +++ src/scopes/internal/zmq_middleware/ZmqScope.cpp 2015-07-08 14:34:02 +0000 |
648 | @@ -325,7 +325,18 @@ |
649 | capnp::MallocMessageBuilder request_builder; |
650 | make_request_(request_builder, "debug_mode"); |
651 | |
652 | - auto future = mw_base()->twoway_pool()->submit([&] { return this->invoke_twoway_(request_builder); }); |
653 | + // When making any two-way request there is an implicit locate() call made to the registry to first ensure that |
654 | + // the scope is actually running - I.e. 1. reg->locate(scope), 2. scope->request(). |
655 | + |
656 | + // Here we are trying to determine whether the target scope is in debug mode, hence we cannot assume upfront |
657 | + // that it will start up within the regular timeout constraint. Therefore we need to disable the locate() |
658 | + // timeout here to allow enough time for debugged scopes to reply. |
659 | + |
660 | + // In the situation where a scope is not in debug mode but starts up slowly, the registry's own |
661 | + // "Process.Timeout" will kick in, meaning that the implicit locate() call will return at a regular timeout for |
662 | + // regular scopes, and will not timeout for debugged scopes. |
663 | + |
664 | + auto future = mw_base()->twoway_pool()->submit([&] { return this->invoke_twoway_(request_builder, timeout(), -1); }); |
665 | auto out_params = future.get(); |
666 | auto response = out_params.reader->getRoot<capnproto::Response>(); |
667 | throw_if_runtime_exception(response); |
668 | @@ -344,11 +355,10 @@ |
669 | |
670 | ZmqObjectProxy::TwowayOutParams ZmqScope::invoke_scope_(capnp::MessageBuilder& in_params, int64_t timeout) |
671 | { |
672 | - // Check if this scope has requested debug mode, if so, disable two-way timeout and set |
673 | - // locate timeout to 15s. |
674 | + // Check if this scope has requested debug mode, if so, disable two-way and locate timeouts. |
675 | if (debug_mode()) |
676 | { |
677 | - return this->invoke_twoway_(in_params, -1, 15000); |
678 | + return this->invoke_twoway_(in_params, -1, -1); |
679 | } |
680 | return this->invoke_twoway_(in_params, timeout); |
681 | } |
682 | |
683 | === modified file 'test/gtest/scopes/Registry/Registry_test.cpp' |
684 | --- test/gtest/scopes/Registry/Registry_test.cpp 2015-04-22 15:41:57 +0000 |
685 | +++ test/gtest/scopes/Registry/Registry_test.cpp 2015-07-08 14:34:02 +0000 |
686 | @@ -153,7 +153,7 @@ |
687 | EXPECT_FALSE(meta.is_aggregator()); |
688 | } |
689 | |
690 | -auto const wait_for_update_time = std::chrono::milliseconds(10000); |
691 | +auto const wait_for_update_time = std::chrono::milliseconds(5000); |
692 | |
693 | TEST(Registry, scope_state_notify) |
694 | { |
695 | |
696 | === modified file 'test/gtest/scopes/internal/CMakeLists.txt' |
697 | --- test/gtest/scopes/internal/CMakeLists.txt 2014-12-17 08:22:07 +0000 |
698 | +++ test/gtest/scopes/internal/CMakeLists.txt 2015-07-08 14:34:02 +0000 |
699 | @@ -16,7 +16,7 @@ |
700 | add_subdirectory(ScopeConfig) |
701 | add_subdirectory(ScopeLoader) |
702 | add_subdirectory(ScopeMetadataImpl) |
703 | -add_subdirectory(SettingsDB) |
704 | +#add_subdirectory(SettingsDB) |
705 | add_subdirectory(smartscopes) |
706 | add_subdirectory(ThreadPool) |
707 | add_subdirectory(ThreadSafeQueue) |
708 | |
709 | === modified file 'test/gtest/scopes/internal/SettingsDB/SettingsDB_test.cpp' |
710 | --- test/gtest/scopes/internal/SettingsDB/SettingsDB_test.cpp 2014-12-09 03:49:46 +0000 |
711 | +++ test/gtest/scopes/internal/SettingsDB/SettingsDB_test.cpp 2015-07-08 14:34:02 +0000 |
712 | @@ -18,30 +18,63 @@ |
713 | * Pete Woods <pete.woods@canonical.com> |
714 | */ |
715 | |
716 | -#define BOOST_NO_CXX11_SCOPED_ENUMS // We need this to successfully link against Boost when calling |
717 | - // copy_file. See https://svn.boost.org/trac/boost/ticket/6779 |
718 | -#include <boost/filesystem/operations.hpp> |
719 | - |
720 | -#undef BOOST_NO_CXX11_SCOPED_ENUMS |
721 | #include <unity/scopes/internal/SettingsDB.h> |
722 | |
723 | #include <unity/UnityExceptions.h> |
724 | |
725 | #include <boost/regex.hpp> // Use Boost implementation until http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53631 is fixed. |
726 | #include <gtest/gtest.h> |
727 | +#include <fcntl.h> |
728 | #include <ctime> |
729 | - |
730 | +#include <thread> |
731 | |
732 | using namespace unity; |
733 | using namespace unity::scopes::internal; |
734 | using namespace std; |
735 | -using namespace boost::filesystem; |
736 | |
737 | string const db_name = TEST_BIN_DIR "/foo.ini"; |
738 | |
739 | void write_db(const string& src) |
740 | { |
741 | - copy_file(string(TEST_SRC_DIR) + "/" + src, db_name, copy_option::overwrite_if_exists); |
742 | + int fd = ::open(db_name.c_str(), O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); |
743 | + if (fd == -1) |
744 | + { |
745 | + FAIL() << "Couldn't open file " << db_name << " " << errno; |
746 | + } |
747 | + |
748 | + struct flock fl; |
749 | + fl.l_whence = SEEK_SET; |
750 | + fl.l_start = 0; |
751 | + fl.l_len = 0; |
752 | + fl.l_type = F_WRLCK; |
753 | + fl.l_pid = getpid(); |
754 | + |
755 | + if (::fcntl(fd, F_SETLKW, &fl) != 0) |
756 | + { |
757 | + FAIL() << "Couldn't get write lock for " << db_name << " " << errno; |
758 | + } |
759 | + |
760 | + string pth = string(TEST_SRC_DIR) + "/" + src; |
761 | + int fd2 = ::open(pth.c_str(), O_RDONLY); |
762 | + if (fd2 == -1) |
763 | + { |
764 | + FAIL() << "Failed to open input file " << pth; |
765 | + } |
766 | + |
767 | + // copy the file |
768 | + int n = 0; |
769 | + char buf[1024]; |
770 | + while ((n = ::read(fd2, buf, 1024)) > 0) |
771 | + { |
772 | + ::write(fd, buf, n); |
773 | + } |
774 | + ::close(fd2); |
775 | + ::close(fd); |
776 | + |
777 | + // make sure the next write doesn't happen too fast or otherwise modification time of settings db |
778 | + // will be the same and change will not be detected by SettingsDB. Note, this is not an issue with |
779 | + // real use cases when settings are modified by the UI (and SettingsDB uses nanosecond-based mtime). |
780 | + std::this_thread::sleep_for(std::chrono::seconds(1)); |
781 | } |
782 | |
783 | #define TRY_EXPECT_EQ(expected, actual) \ |
784 | @@ -349,9 +382,9 @@ |
785 | db->settings(); |
786 | FAIL(); |
787 | } |
788 | - catch (SyscallException const& e) |
789 | + catch (FileException const& e) |
790 | { |
791 | - boost::regex r("unity::SyscallException: SettingsDB::add_watch\\(\\): failed to add watch for path: \".*\". inotify_add_watch\\(\\) failed. \\(fd = [0-9]+, path = .*\\) \\(errno = 13\\)"); |
792 | + boost::regex r("unity::FileException: Couldn't open file .* \\(errno = 13\\)"); |
793 | EXPECT_TRUE(boost::regex_match(e.what(), r)) << e.what(); |
794 | } |
795 |
FAILED: Continuous integration, rev:292 jenkins. qa.ubuntu. com/job/ unity-scopes- api-ci/ 616/ jenkins. qa.ubuntu. com/job/ unity-scopes- api-wily- amd64-ci/ 4 jenkins. qa.ubuntu. com/job/ unity-scopes- api-wily- armhf-ci/ 4/console jenkins. qa.ubuntu. com/job/ unity-scopes- api-wily- i386-ci/ 4
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scopes- api-ci/ 616/rebuild
http://