Merge lp:unity-scopes-api/staging into lp:unity-scopes-api

Proposed by Paweł Stołowski
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
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Unity Team Pending
Review via email: mp+263389@code.launchpad.net

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.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:unity-scopes-api/staging updated
293. By Paweł Stołowski

Merged devel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:unity-scopes-api/staging updated
294. By Paweł Stołowski

Merged devel

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:unity-scopes-api/staging updated
295. By Paweł Stołowski

Merged lp:~stolowski/unity-scopes-api/disable-settings-test

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches

to all changes: